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 reason to onFilterModelChange #4938

Merged
merged 5 commits into from
Jun 7, 2022
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
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: 1 addition & 1 deletion docs/pages/x/api/data-grid/grid-api.md
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ import { GridApi } from '@mui/x-data-grid-pro';
| <span class="prop-name">setEditRowsModel</span> | <span class="prop-type">(model: GridEditRowsModel) =&gt; void</span> | Set the edit rows model of the grid. |
| <span class="prop-name">setExpandedDetailPanels [<span class="plan-pro" title="Pro plan"></span>](https://mui.com/store/items/material-ui-pro/)</span> | <span class="prop-type">(ids: GridRowId[]) =&gt; void</span> | Changes which rows to expand the detail panel. |
| <span class="prop-name">setFilterLinkOperator</span> | <span class="prop-type">(operator: GridLinkOperator) =&gt; void</span> | Changes the GridLinkOperator used to connect the filters. |
| <span class="prop-name">setFilterModel</span> | <span class="prop-type">(model: GridFilterModel) =&gt; void</span> | Sets the filter model to the one given by `model`. |
| <span class="prop-name">setFilterModel</span> | <span class="prop-type">(model: GridFilterModel, reason?: GridControlledStateReasonLookup['filter']) =&gt; void</span> | Sets the filter model to the one given by `model`. |
| <span class="prop-name">setPage</span> | <span class="prop-type">(page: number) =&gt; void</span> | Sets the displayed page to the value given by `page`. |
| <span class="prop-name">setPageSize</span> | <span class="prop-type">(pageSize: number) =&gt; void</span> | Sets the number of displayed rows to the value given by `pageSize`. |
| <span class="prop-name">setPinnedColumns [<span class="plan-pro" title="Pro plan"></span>](https://mui.com/store/items/material-ui-pro/)</span> | <span class="prop-type">(pinnedColumns: GridPinnedColumns) =&gt; void</span> | Changes the pinned columns. |
Expand Down
2 changes: 1 addition & 1 deletion docs/pages/x/api/data-grid/grid-filter-api.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
{
"name": "setFilterModel",
"description": "Sets the filter model to the one given by <code>model</code>.",
"type": "(model: GridFilterModel) => void"
"type": "(model: GridFilterModel, reason?: GridControlledStateReasonLookup['filter']) => void"
},
{
"name": "setQuickFilterValues",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ export const useGridRowGrouping = (
| 'disableRowGrouping'
>,
) => {
apiRef.current.unstable_updateControlState({
apiRef.current.unstable_registerControlState({
stateId: 'rowGrouping',
propModel: props.rowGroupingModel,
propOnChange: props.onRowGroupingModelChange,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -243,7 +243,7 @@ export const useGridColumnPinning = (
useGridRegisterPipeProcessor(apiRef, 'exportState', stateExportPreProcessing);
useGridRegisterPipeProcessor(apiRef, 'restoreState', stateRestorePreProcessing);

apiRef.current.unstable_updateControlState({
apiRef.current.unstable_registerControlState({
stateId: 'pinnedColumns',
propModel: props.pinnedColumns,
propOnChange: props.onPinnedColumnsChange,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ export const useGridDetailPanel = (

useGridRegisterPipeProcessor(apiRef, 'rowHeight', addDetailHeight);

apiRef.current.unstable_updateControlState({
apiRef.current.unstable_registerControlState({
stateId: 'detailPanels',
propModel: props.detailPanelExpandedRowIds,
propOnChange: props.onDetailPanelExpandedRowIdsChange,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ export const useGridRowGrouping = (
| 'disableRowGrouping'
>,
) => {
apiRef.current.unstable_updateControlState({
apiRef.current.unstable_registerControlState({
stateId: 'rowGrouping',
propModel: props.rowGroupingModel,
propOnChange: props.onRowGroupingModelChange,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,7 @@ describe('<DataGridPro /> - Filter', () => {
expect(getColumnValues(0)).to.deep.equal(['Adidas', 'Puma']);
});

it('should trigger onFilterModelChange when the link operator changes but not change the state', () => {
it("should call onFilterModelChange with reason=changeLogicOperator when the logic operator changes but doesn't change the state", () => {
const onFilterModelChange = spy();
const newModel: GridFilterModel = {
items: [
Expand Down Expand Up @@ -219,10 +219,11 @@ describe('<DataGridPro /> - Filter', () => {
];
fireEvent.change(select, { target: { value: 'or' } });
expect(onFilterModelChange.callCount).to.equal(1);
expect(onFilterModelChange.lastCall.args[1].reason).to.equal('changeLogicOperator');
expect(getColumnValues(0)).to.deep.equal([]);
});

it('should call onFilterModelChange when the value is emptied', () => {
it('should call onFilterModelChange with reason=upsertFilterItem when the value is emptied', () => {
const onFilterModelChange = spy();
render(
<TestCase
Expand All @@ -246,6 +247,74 @@ describe('<DataGridPro /> - Filter', () => {
fireEvent.change(screen.queryByRole('textbox', { name: 'Value' }), { target: { value: '' } });
clock.tick(500);
expect(onFilterModelChange.callCount).to.equal(1);
expect(onFilterModelChange.lastCall.args[1].reason).to.equal('upsertFilterItem');
});

it('should call onFilterModelChange with reason=deleteFilterItem when a filter is removed', () => {
const onFilterModelChange = spy();
render(
<TestCase
onFilterModelChange={onFilterModelChange}
filterModel={{
items: [
{
id: 1,
columnField: 'brand',
value: 'a',
operatorValue: 'contains',
},
{
id: 2,
columnField: 'brand',
value: 'a',
operatorValue: 'endsWith',
},
],
}}
initialState={{
preferencePanel: { openedPanelValue: GridPreferencePanelsValue.filters, open: true },
}}
/>,
);
expect(onFilterModelChange.callCount).to.equal(0);
fireEvent.click(screen.queryAllByRole('button', { name: 'Delete' })[0]);
expect(onFilterModelChange.callCount).to.equal(1);
expect(onFilterModelChange.lastCall.args[1].reason).to.equal('deleteFilterItem');
});

it('should call onFilterModelChange with reason=addFilterItem when a filter is added', () => {
const onFilterModelChange = spy();
render(
<TestCase
onFilterModelChange={onFilterModelChange}
filterModel={{
items: [{ id: 1, columnField: 'brand', value: 'a', operatorValue: 'contains' }],
}}
initialState={{
preferencePanel: { openedPanelValue: GridPreferencePanelsValue.filters, open: true },
}}
/>,
);
expect(onFilterModelChange.callCount).to.equal(0);
fireEvent.click(screen.queryByRole('button', { name: 'Add filter' }));
expect(onFilterModelChange.callCount).to.equal(1);
expect(onFilterModelChange.lastCall.args[1].reason).to.equal('addFilterItem');
});

it('should publish filterModelChange with the reason whenever the model changes', () => {
const listener = spy();
render(
<TestCase
initialState={{
preferencePanel: { openedPanelValue: GridPreferencePanelsValue.filters, open: true },
}}
/>,
);
apiRef.current.subscribeEvent('filterModelChange', listener);
expect(listener.callCount).to.equal(0);
fireEvent.click(screen.queryByRole('button', { name: 'Add filter' }));
expect(listener.callCount).to.equal(1);
expect(listener.lastCall.args[1].reason).to.equal('addFilterItem');
});

it('should only select visible rows', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,10 @@ function GridFilterPanel(props: GridFilterPanelProps) {
if (!defaultItem) {
return;
}
apiRef.current.setFilterModel({ ...filterModel, items: [...items, defaultItem] });
apiRef.current.setFilterModel(
{ ...filterModel, items: [...items, defaultItem] },
'addFilterItem',
);
};

const deleteFilter = React.useCallback(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ export const useGridStateInitialization = <Api extends GridApiCommon>(
);
const [, rawForceUpdate] = React.useState<Api['state']>();

const updateControlState = React.useCallback<
GridStateApi<Api['state']>['unstable_updateControlState']
const registerControlState = React.useCallback<
GridStateApi<Api['state']>['unstable_registerControlState']
>((controlStateItem) => {
const { stateId, ...others } = controlStateItem;

Expand All @@ -28,7 +28,7 @@ export const useGridStateInitialization = <Api extends GridApiCommon>(
}, []);

const setState = React.useCallback<GridStateApi<Api['state']>['setState']>(
(state) => {
(state, reason) => {
let newState: Api['state'];
if (isFunction(state)) {
newState = state(apiRef.current.state);
Expand Down Expand Up @@ -96,12 +96,14 @@ export const useGridStateInitialization = <Api extends GridApiCommon>(

if (controlState.propOnChange && hasPropChanged) {
const details =
props.signature === GridSignature.DataGridPro ? { api: apiRef.current } : {};
props.signature === GridSignature.DataGridPro
? { api: apiRef.current, reason }
: { reason };
controlState.propOnChange(model, details);
}

if (!ignoreSetState) {
apiRef.current.publishEvent(controlState.changeEvent, model);
apiRef.current.publishEvent(controlState.changeEvent, model, { reason });
}
}

Expand All @@ -110,12 +112,24 @@ export const useGridStateInitialization = <Api extends GridApiCommon>(
[apiRef, props.signature],
);

const updateControlState = React.useCallback<
GridStateApi<Api['state']>['unstable_updateControlState']
>(
(key, state, reason) => {
return apiRef.current.setState((previousState: Api['state']) => {
return { ...previousState, [key]: state(previousState[key]) };
}, reason);
},
[apiRef],
);

const forceUpdate = React.useCallback(() => rawForceUpdate(() => apiRef.current.state), [apiRef]);

const stateApi: any = {
setState,
forceUpdate,
unstable_updateControlState: updateControlState,
unstable_registerControlState: registerControlState,
};

useGridApiMethod(apiRef, stateApi, 'GridStateApi');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ export function useGridColumns(
!!props.columnVisibilityModel || !!props.initialState?.columns?.columnVisibilityModel,
);

apiRef.current.unstable_updateControlState({
apiRef.current.unstable_registerControlState({
stateId: 'visibleColumns',
propModel: props.columnVisibilityModel,
propOnChange: props.onColumnVisibilityModelChange,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ export function useGridEditing(
{},
);

apiRef.current.unstable_updateControlState({
apiRef.current.unstable_registerControlState({
stateId: 'editRows',
propModel: props.editRowsModel,
propOnChange: props.onEditRowsModelChange,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -111,12 +111,9 @@ export const mergeStateWithFilterModel =
disableMultipleColumnsFiltering: boolean,
apiRef: React.MutableRefObject<GridApiCommunity>,
) =>
(state: GridStateCommunity): GridStateCommunity => ({
...state,
filter: {
...state.filter,
filterModel: sanitizeFilterModel(filterModel, disableMultipleColumnsFiltering, apiRef),
},
(filteringState: GridStateCommunity['filter']): GridStateCommunity['filter'] => ({
...filteringState,
filterModel: sanitizeFilterModel(filterModel, disableMultipleColumnsFiltering, apiRef),
});

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ export const useGridFilter = (
): void => {
const logger = useGridLogger(apiRef, 'useGridFilter');

apiRef.current.unstable_updateControlState({
apiRef.current.unstable_registerControlState({
stateId: 'filter',
propModel: props.filterModel,
propOnChange: props.onFilterModelChange,
Expand Down Expand Up @@ -114,7 +114,7 @@ export const useGridFilter = (
} else {
items[itemIndex] = item;
}
apiRef.current.setFilterModel({ ...filterModel, items });
apiRef.current.setFilterModel({ ...filterModel, items }, 'upsertFilterItem');
},
[apiRef],
);
Expand All @@ -128,7 +128,7 @@ export const useGridFilter = (
return;
}

apiRef.current.setFilterModel({ ...filterModel, items });
apiRef.current.setFilterModel({ ...filterModel, items }, 'deleteFilterItem');
},
[apiRef],
);
Expand Down Expand Up @@ -174,10 +174,13 @@ export const useGridFilter = (
if (filterModel.linkOperator === linkOperator) {
return;
}
apiRef.current.setFilterModel({
...filterModel,
linkOperator,
});
apiRef.current.setFilterModel(
{
...filterModel,
linkOperator,
},
'changeLogicOperator',
);
},
[apiRef],
);
Expand All @@ -197,12 +200,14 @@ export const useGridFilter = (
);

const setFilterModel = React.useCallback<GridFilterApi['setFilterModel']>(
(model) => {
(model, reason) => {
const currentModel = gridFilterModelSelector(apiRef);
if (currentModel !== model) {
logger.debug('Setting filter model');
apiRef.current.setState(
apiRef.current.unstable_updateControlState(
'filter',
mergeStateWithFilterModel(model, props.disableMultipleColumnsFiltering, apiRef),
reason,
);
apiRef.current.unstable_applyFilters();
}
Expand Down Expand Up @@ -258,8 +263,10 @@ export const useGridFilter = (
if (filterModel == null) {
return params;
}
apiRef.current.setState(
apiRef.current.unstable_updateControlState(
'filter',
mergeStateWithFilterModel(filterModel, props.disableMultipleColumnsFiltering, apiRef),
'restoreState',
);

return {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ export const useGridPage = (

const visibleTopLevelRowCount = useGridSelector(apiRef, gridVisibleTopLevelRowCountSelector);

apiRef.current.unstable_updateControlState({
apiRef.current.unstable_registerControlState({
stateId: 'page',
propModel: props.page,
propOnChange: props.onPageChange,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ export const useGridPageSize = (
const logger = useGridLogger(apiRef, 'useGridPageSize');
const rowHeight = useGridSelector(apiRef, gridDensityRowHeightSelector);

apiRef.current.unstable_updateControlState({
apiRef.current.unstable_registerControlState({
stateId: 'pageSize',
propModel: props.pageSize,
propOnChange: props.onPageSizeChange,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ export const useGridSelection = (

const lastRowToggled = React.useRef<GridRowId | null>(null);

apiRef.current.unstable_updateControlState({
apiRef.current.unstable_registerControlState({
stateId: 'selection',
propModel: propSelectionModel,
propOnChange: props.onSelectionModelChange,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ export const useGridSorting = (
) => {
const logger = useGridLogger(apiRef, 'useGridSorting');

apiRef.current.unstable_updateControlState({
apiRef.current.unstable_registerControlState({
stateId: 'sortModel',
propModel: props.sortModel,
propOnChange: props.onSortModelChange,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,16 @@
import { GridControlledStateReasonLookup } from '../events/gridEventLookup';

/**
* Additional details passed to the callbacks
*/
export interface GridCallbackDetails {
export interface GridCallbackDetails<K extends keyof GridControlledStateReasonLookup = any> {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Once apiRef.current.unstable_updateControlState stops being only a wrapper for setState we can replace the any with the correct types. The any is necessary because we don't know which key of the state was changed to match with GridControlledStateReasonLookup.

/**
* Provided only if `DataGridPro` is being used.
* @deprecated Use the `apiRef` returned by `useGridApiContext` or `useGridApiRef` (only available in `@mui/x-data-grid-pro`)
*/
api?: any;
/**
* The reason for this callback to have been called.
*/
reason?: GridControlledStateReasonLookup[K];
}
Loading