Skip to content

Commit

Permalink
[DataGrid] Add reason to onFilterModelChange (mui#4938)
Browse files Browse the repository at this point in the history
  • Loading branch information
m4theushw authored and alexfauquette committed Aug 26, 2022
1 parent fe1ae5a commit 49b388b
Show file tree
Hide file tree
Showing 24 changed files with 198 additions and 41 deletions.
3 changes: 2 additions & 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/mui-x-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/mui-x-pro/)</span> | <span class="prop-type">(pinnedColumns: GridPinnedColumns) =&gt; void</span> | Changes the pinned columns. |
Expand Down Expand Up @@ -120,3 +120,4 @@ import { GridApi } from '@mui/x-data-grid-pro';
| <span class="prop-name">updateColumns</span> | <span class="prop-type">(cols: GridColDef[]) =&gt; void</span> | Updates the definition of multiple columns at the same time. |
| <span class="prop-name">updateRows</span> | <span class="prop-type">(updates: GridRowModelUpdate[]) =&gt; void</span> | Allows to updates, insert and delete rows in a single call. |
| <span class="prop-name">upsertFilterItem</span> | <span class="prop-type">(item: GridFilterItem) =&gt; void</span> | Updates or inserts a [GridFilterItem](/x/api/data-grid/grid-filter-item/). |
| <span class="prop-name">upsertFilterItems</span> | <span class="prop-type">(items: GridFilterItem[]) =&gt; void</span> | Updates or inserts many [GridFilterItem](/x/api/data-grid/grid-filter-item/). |
7 changes: 6 additions & 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 All @@ -37,6 +37,11 @@
"name": "upsertFilterItem",
"description": "Updates or inserts a <a href=\"/x/api/data-grid/grid-filter-item/\">GridFilterItem</a>.",
"type": "(item: GridFilterItem) => void"
},
{
"name": "upsertFilterItems",
"description": "Updates or inserts many <a href=\"/x/api/data-grid/grid-filter-item/\">GridFilterItem</a>.",
"type": "(items: GridFilterItem[]) => void"
}
]
}
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,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 @@ -248,7 +248,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 @@ -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=upsertFilterItems 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('upsertFilterItems');
});

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('upsertFilterItems');
});

it('should only select visible rows', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ const GridFilterPanel = React.forwardRef<HTMLDivElement, GridFilterPanelProps>(
if (!defaultItem) {
return;
}
apiRef.current.setFilterModel({ ...filterModel, items: [...items, defaultItem] });
apiRef.current.upsertFilterItems([...items, defaultItem]);
};

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 @@ -101,7 +101,7 @@ export function useGridColumns(
const previousColumnsProp = React.useRef(props.columns);
const previousColumnTypesProp = React.useRef(columnTypes);

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 @@ -66,7 +66,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 @@ -115,7 +115,24 @@ export const useGridFilter = (
} else {
items[itemIndex] = item;
}
apiRef.current.setFilterModel({ ...filterModel, items });
apiRef.current.setFilterModel({ ...filterModel, items }, 'upsertFilterItem');
},
[apiRef],
);

const upsertFilterItems = React.useCallback<GridFilterApi['upsertFilterItems']>(
(items) => {
const filterModel = gridFilterModelSelector(apiRef);
const existingItems = [...filterModel.items];
items.forEach((item) => {
const itemIndex = items.findIndex((filterItem) => filterItem.id === item.id);
if (itemIndex === -1) {
existingItems.push(item);
} else {
existingItems[itemIndex] = item;
}
});
apiRef.current.setFilterModel({ ...filterModel, items }, 'upsertFilterItems');
},
[apiRef],
);
Expand All @@ -129,7 +146,7 @@ export const useGridFilter = (
return;
}

apiRef.current.setFilterModel({ ...filterModel, items });
apiRef.current.setFilterModel({ ...filterModel, items }, 'deleteFilterItem');
},
[apiRef],
);
Expand Down Expand Up @@ -178,10 +195,13 @@ export const useGridFilter = (
if (filterModel.linkOperator === linkOperator) {
return;
}
apiRef.current.setFilterModel({
...filterModel,
linkOperator,
});
apiRef.current.setFilterModel(
{
...filterModel,
linkOperator,
},
'changeLogicOperator',
);
},
[apiRef],
);
Expand All @@ -201,12 +221,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 All @@ -224,6 +246,7 @@ export const useGridFilter = (
unstable_applyFilters: applyFilters,
deleteFilterItem,
upsertFilterItem,
upsertFilterItems,
setFilterModel,
showFilterPanel,
hideFilterPanel,
Expand Down Expand Up @@ -262,8 +285,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
Loading

0 comments on commit 49b388b

Please sign in to comment.