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 4 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
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 @@ -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 @@ -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 @@ -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,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 @@ -128,7 +145,7 @@ export const useGridFilter = (
return;
}

apiRef.current.setFilterModel({ ...filterModel, items });
apiRef.current.setFilterModel({ ...filterModel, items }, 'deleteFilterItem');
},
[apiRef],
);
Expand Down Expand Up @@ -174,10 +191,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 +217,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 @@ -220,6 +242,7 @@ export const useGridFilter = (
unstable_applyFilters: applyFilters,
deleteFilterItem,
upsertFilterItem,
upsertFilterItems,
setFilterModel,
showFilterPanel,
hideFilterPanel,
Expand Down Expand Up @@ -258,8 +281,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