Skip to content

Commit

Permalink
[XGrid] Fix server-side multi filters (#1029)
Browse files Browse the repository at this point in the history
  • Loading branch information
dtassone authored Feb 12, 2021
1 parent 17f7a33 commit f11f266
Show file tree
Hide file tree
Showing 3 changed files with 145 additions and 41 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@ export const useFilter = (apiRef: ApiRef, rowsProp: RowsProp): void => {

const applyFilters = React.useCallback(() => {
if (options.filterMode === FeatureModeConstant.server) {
forceUpdate();
return;
}

Expand Down
94 changes: 87 additions & 7 deletions packages/grid/x-grid/src/tests/filtering.XGrid.test.tsx
Original file line number Diff line number Diff line change
@@ -1,20 +1,24 @@
import { expect } from 'chai';
import * as React from 'react';
import { expect } from 'chai';
import { useFakeTimers } from 'sinon';
import { getColumnHeaderCell, getColumnValues } from 'test/utils/helperFn';
import {
createClientRenderStrictMode,
// @ts-expect-error need to migrate helpers to TypeScript
fireEvent,
} from 'test/utils';
import {
ApiRef,
FilterModel,
GridComponentProps,
LinkOperator,
PreferencePanelsValue,
RowModel,
useApiRef,
XGrid,
} from '@material-ui/x-grid';
import {
// @ts-expect-error need to migrate helpers to TypeScript
screen,
createClientRenderStrictMode,
// @ts-expect-error need to migrate helpers to TypeScript
fireEvent,
} from 'test/utils';
import { getColumnHeaderCell, getColumnValues } from 'test/utils/helperFn';

describe('<XGrid /> - Filter', () => {
let clock;
Expand Down Expand Up @@ -224,4 +228,80 @@ describe('<XGrid /> - Filter', () => {
setProps({ filterModel: { items: [] } });
expect(getColumnValues()).to.deep.equal(['Nike', 'Adidas', 'Puma']);
});

describe('Server', () => {
it('should refresh the filter panel when adding filters', () => {
function loadServerRows(commodityFilterValue) {
const serverRows = [
{ id: '1', commodity: 'rice' },
{ id: '2', commodity: 'soybeans' },
{ id: '3', commodity: 'milk' },
{ id: '4', commodity: 'wheat' },
{ id: '5', commodity: 'oats' },
];

return new Promise<RowModel[]>((resolve) => {
if (!commodityFilterValue) {
resolve(serverRows);
return;
}
resolve(
serverRows.filter(
(row) => row.commodity.toLowerCase().indexOf(commodityFilterValue) > -1,
),
);
});
}

const columns = [{ field: 'commodity', width: 150 }];

function AddServerFilterGrid() {
const [rows, setRows] = React.useState<RowModel[]>([]);
const [filterValue, setFilterValue] = React.useState();

const onFilterChange = React.useCallback((params) => {
setFilterValue(params.filterModel.items[0].value);
}, []);

React.useEffect(() => {
let active = true;

(async () => {
const newRows = await loadServerRows(filterValue);

if (!active) {
return;
}

setRows(newRows);
})();

return () => {
active = false;
};
}, [filterValue]);

return (
<div style={{ height: 400, width: 400 }}>
<XGrid
rows={rows}
columns={columns}
filterMode="server"
onFilterModelChange={onFilterChange}
state={{
preferencePanel: { open: true, openedPanelValue: PreferencePanelsValue.filters },
}}
/>
</div>
);
}

render(<AddServerFilterGrid />);
const addButton = screen.getByRole('button', { name: /Add Filter/i });
clock.tick(100);
fireEvent.click(addButton);
const filterForms = document.querySelectorAll(`.MuiDataGridFilterForm-root`);
expect(filterForms).to.have.length(2);
});
});
});
91 changes: 57 additions & 34 deletions packages/storybook/src/stories/grid-filter.stories.tsx
Original file line number Diff line number Diff line change
@@ -1,25 +1,25 @@
import * as React from 'react';
import Button from '@material-ui/core/Button';
import InputAdornment from '@material-ui/core/InputAdornment';
import { makeStyles } from '@material-ui/core/styles';
import { DataGrid } from '@material-ui/data-grid';
import Rating from '@material-ui/lab/Rating';
import Button from '@material-ui/core/Button';
import {
ColDef,
ColTypeDef,
FilterInputValueProps,
FilterModel,
FilterItem,
FilterModel,
FilterModelParams,
getNumericColumnOperators,
LinkOperator,
PreferencePanelsValue,
RowModel,
useApiRef,
XGrid,
getNumericColumnOperators,
FilterModelParams,
} from '@material-ui/x-grid';
import { useDemoData } from '@material-ui/x-grid-data-generator';
import { action } from '@storybook/addon-actions';
import InputAdornment from '@material-ui/core/InputAdornment';
import * as React from 'react';
import { randomInt } from '../data/random-generator';
import { useData } from '../hooks/useData';

Expand All @@ -34,10 +34,6 @@ export default {
},
};

// server filters
// with new rows from apiRef
// with new columns (partial and complete)

export function CommodityWithOpenFilters() {
const { data } = useDemoData({ dataSet: 'Commodity', rowLength: 500 });

Expand Down Expand Up @@ -421,21 +417,31 @@ function RatingInputValue(props: FilterInputValueProps) {

export function CustomFilterOperator() {
const { data } = useDemoData({ dataSet: 'Employee', rowLength: 100 });
const [columns, setColumns] = React.useState(data.columns);

React.useEffect(() => {
if (data.columns.length > 0) {
let newColumns: ColDef[] = [...data.columns];
const ratingColumn = { ...newColumns.find((col) => col.field === 'rating') };

const ratingOperators = getNumericColumnOperators();
ratingColumn!.filterOperators = ratingOperators.map((operator) => {
operator.InputComponent = RatingInputValue;
return operator;
});

if (data.columns.length > 0) {
const ratingColumn = data.columns.find((col) => col.field === 'rating');
const ratingOperators = getNumericColumnOperators();
ratingColumn!.filterOperators = ratingOperators.map((operator) => {
operator.InputComponent = RatingInputValue;
return operator;
});
}
newColumns = newColumns.map((col) =>
col.field === 'rating' ? ratingColumn : col,
) as ColDef[];
setColumns(newColumns);
}
}, [data.columns]);

return (
<div className="grid-container">
<XGrid
rows={data.rows}
columns={data.columns}
columns={columns}
filterModel={{
items: [{ columnField: 'rating', value: '3.5', operatorValue: '>=' }],
}}
Expand Down Expand Up @@ -470,17 +476,25 @@ const RatingOnlyOperators = [

export function RatingOperator() {
const { data } = useDemoData({ dataSet: 'Employee', rowLength: 100 });
const [columns, setColumns] = React.useState(data.columns);

if (data.columns.length > 0) {
const ratingColumn = data.columns.find((col) => col.field === 'rating');
ratingColumn!.filterOperators = RatingOnlyOperators;
}

React.useEffect(() => {
if (data.columns.length > 0) {
let newColumns: ColDef[] = [...data.columns];
const ratingColumn = { ...newColumns.find((col) => col.field === 'rating') };
ratingColumn!.filterOperators = RatingOnlyOperators;

newColumns = newColumns.map((col) =>
col.field === 'rating' ? ratingColumn : col,
) as ColDef[];
setColumns(newColumns);
}
}, [data.columns]);
return (
<div className="grid-container">
<XGrid
rows={data.rows}
columns={data.columns}
columns={columns}
filterModel={{
items: [{ columnField: 'rating', value: '3.5', operatorValue: 'from' }],
}}
Expand Down Expand Up @@ -560,7 +574,15 @@ export function NewColumnTypes() {

return (
<div className="grid-container">
<DataGrid rows={data.rows} columns={cols} columnTypes={{ price: priceColumnType }} />
<DataGrid
rows={data.rows}
columns={cols}
columnTypes={{ price: priceColumnType }}
filterModel={{
items: [{ id: 1, columnField: 'totalPrice', operatorValue: '>', value: '1000000' }],
}}
state={{ preferencePanel: { openedPanelValue: PreferencePanelsValue.filters, open: true } }}
/>
</div>
);
}
Expand All @@ -571,29 +593,30 @@ const filterModel = {

export function DemoCustomRatingFilterOperator() {
const { data } = useDemoData({ dataSet: 'Employee', rowLength: 100 });
const [columns, setColumns] = React.useState(data.columns);

React.useEffect(() => {
if (data.columns.length > 0) {
const ratingColumn = data.columns.find((col) => col.field === 'rating');
let newColumns = [...data.columns];
const ratingColumn = { ...newColumns.find((col) => col.field === 'rating') };

const ratingOperators = getNumericColumnOperators();
ratingColumn!.filterOperators = ratingOperators.map((operator) => {
operator.InputComponent = RatingInputValue;
return operator;
});

// Just hidding some columns for demo clarity
data.columns
.filter((col) => col.field === 'phone' || col.field === 'email' || col.field === 'username')
.forEach((col) => {
col.hide = true;
});
newColumns = newColumns.map((col) =>
col.field === 'rating' ? ratingColumn : col,
) as ColDef[];

setColumns(newColumns);
}
}, [data.columns]);

return (
<div style={{ height: 400, width: '100%' }}>
<DataGrid rows={data.rows} columns={data.columns} filterModel={filterModel} />
<DataGrid rows={data.rows} columns={columns} filterModel={filterModel} />
</div>
);
}
Expand Down

0 comments on commit f11f266

Please sign in to comment.