Skip to content

Commit

Permalink
[DataGrid] Update quick filter input when model is modified (#5013)
Browse files Browse the repository at this point in the history
Co-authored-by: Matheus Wichman <matheushw@outlook.com>
  • Loading branch information
alexfauquette and m4theushw authored Jun 3, 2022
1 parent 1c4e6e8 commit 23aaa6b
Show file tree
Hide file tree
Showing 18 changed files with 186 additions and 21 deletions.
5 changes: 5 additions & 0 deletions docs/data/data-grid/filtering/filtering.md
Original file line number Diff line number Diff line change
Expand Up @@ -376,13 +376,18 @@ The values used by the quick filter are obtained by splitting with space.
If you want to implement a more advanced logic, the `<GridToolbarQuickFilter/>` component accepts a prop `quickFilterParser`.
This function takes the string from the search text field and returns an array of values.

If you control the `quickFilterValues` either by controlling `filterModel` or with the initial state, the content of the input must be updated to reflect the new values.
By default, values are joint with a spaces. You can customize this behavior by providing `quickFilterFormatter`.
This formatter can be seen as the inverse of the `quickFilterParser`.

For example, the following parser allows to search words containing a space by using the `','` to split values.

```jsx
<GridToolbarQuickFilter
quickFilterParser={(searchInput) =>
searchInput.split(',').map((value) => value.trim())
}
quickFilterFormatter={(quickFilterValues) => quickFilterValues.join(', ')}
debounceMs={200} // time before applying the new quick filter value
/>
```
Expand Down
1 change: 1 addition & 0 deletions docs/pages/x/api/data-grid/data-grid-pro.json
Original file line number Diff line number Diff line change
Expand Up @@ -291,6 +291,7 @@
"Pagination": { "default": "Pagination", "type": { "name": "elementType | null" } },
"Panel": { "default": "GridPanel", "type": { "name": "elementType" } },
"PreferencesPanel": { "default": "GridPreferencesPanel", "type": { "name": "elementType" } },
"QuickFilterClearIcon": { "default": "GridCloseIcon", "type": { "name": "elementType" } },
"QuickFilterIcon": { "default": "GridSearchIcon", "type": { "name": "elementType" } },
"Row": { "default": "GridRow", "type": { "name": "elementType" } },
"RowReorderIcon": { "default": "GridDragIcon", "type": { "name": "elementType" } },
Expand Down
1 change: 1 addition & 0 deletions docs/pages/x/api/data-grid/data-grid.json
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,7 @@
"Pagination": { "default": "Pagination", "type": { "name": "elementType | null" } },
"Panel": { "default": "GridPanel", "type": { "name": "elementType" } },
"PreferencesPanel": { "default": "GridPreferencesPanel", "type": { "name": "elementType" } },
"QuickFilterClearIcon": { "default": "GridCloseIcon", "type": { "name": "elementType" } },
"QuickFilterIcon": { "default": "GridSearchIcon", "type": { "name": "elementType" } },
"Row": { "default": "GridRow", "type": { "name": "elementType" } },
"RowReorderIcon": { "default": "GridDragIcon", "type": { "name": "elementType" } },
Expand Down
7 changes: 7 additions & 0 deletions docs/pages/x/api/data-grid/selectors.json
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,13 @@
"description": "",
"supportsApiRef": false
},
{
"name": "gridQuickFilterValuesSelector",
"returnType": "any[] | undefined",
"category": "Filtering",
"description": "Get the current quick filter values.",
"supportsApiRef": true
},
{
"name": "gridResizingColumnFieldSelector",
"returnType": "string",
Expand Down
3 changes: 2 additions & 1 deletion docs/translations/api-docs/data-grid/data-grid-pro-pt.json
Original file line number Diff line number Diff line change
Expand Up @@ -614,6 +614,7 @@
"DetailPanelCollapseIcon": "Icon displayed on the detail panel toggle column when expanded.",
"FilterPanelDeleteIcon": "Icon displayed for deleting the filter from filter Panel.",
"RowReorderIcon": "Icon displayed on the <code>reorder</code> column type to reorder a row.",
"QuickFilterIcon": "Icon displayed on the quick filter input."
"QuickFilterIcon": "Icon displayed on the quick filter input.",
"QuickFilterClearIcon": "Icon displayed on the quick filter reset input."
}
}
3 changes: 2 additions & 1 deletion docs/translations/api-docs/data-grid/data-grid-pro-zh.json
Original file line number Diff line number Diff line change
Expand Up @@ -614,6 +614,7 @@
"DetailPanelCollapseIcon": "Icon displayed on the detail panel toggle column when expanded.",
"FilterPanelDeleteIcon": "Icon displayed for deleting the filter from filter Panel.",
"RowReorderIcon": "Icon displayed on the <code>reorder</code> column type to reorder a row.",
"QuickFilterIcon": "Icon displayed on the quick filter input."
"QuickFilterIcon": "Icon displayed on the quick filter input.",
"QuickFilterClearIcon": "Icon displayed on the quick filter reset input."
}
}
3 changes: 2 additions & 1 deletion docs/translations/api-docs/data-grid/data-grid-pro.json
Original file line number Diff line number Diff line change
Expand Up @@ -614,6 +614,7 @@
"DetailPanelCollapseIcon": "Icon displayed on the detail panel toggle column when expanded.",
"FilterPanelDeleteIcon": "Icon displayed for deleting the filter from filter Panel.",
"RowReorderIcon": "Icon displayed on the <code>reorder</code> column type to reorder a row.",
"QuickFilterIcon": "Icon displayed on the quick filter input."
"QuickFilterIcon": "Icon displayed on the quick filter input.",
"QuickFilterClearIcon": "Icon displayed on the quick filter reset input."
}
}
3 changes: 2 additions & 1 deletion docs/translations/api-docs/data-grid/data-grid-pt.json
Original file line number Diff line number Diff line change
Expand Up @@ -585,6 +585,7 @@
"DetailPanelCollapseIcon": "Icon displayed on the detail panel toggle column when expanded.",
"FilterPanelDeleteIcon": "Icon displayed for deleting the filter from filter Panel.",
"RowReorderIcon": "Icon displayed on the <code>reorder</code> column type to reorder a row.",
"QuickFilterIcon": "Icon displayed on the quick filter input."
"QuickFilterIcon": "Icon displayed on the quick filter input.",
"QuickFilterClearIcon": "Icon displayed on the quick filter reset input."
}
}
3 changes: 2 additions & 1 deletion docs/translations/api-docs/data-grid/data-grid-zh.json
Original file line number Diff line number Diff line change
Expand Up @@ -585,6 +585,7 @@
"DetailPanelCollapseIcon": "Icon displayed on the detail panel toggle column when expanded.",
"FilterPanelDeleteIcon": "Icon displayed for deleting the filter from filter Panel.",
"RowReorderIcon": "Icon displayed on the <code>reorder</code> column type to reorder a row.",
"QuickFilterIcon": "Icon displayed on the quick filter input."
"QuickFilterIcon": "Icon displayed on the quick filter input.",
"QuickFilterClearIcon": "Icon displayed on the quick filter reset input."
}
}
3 changes: 2 additions & 1 deletion docs/translations/api-docs/data-grid/data-grid.json
Original file line number Diff line number Diff line change
Expand Up @@ -585,6 +585,7 @@
"DetailPanelCollapseIcon": "Icon displayed on the detail panel toggle column when expanded.",
"FilterPanelDeleteIcon": "Icon displayed for deleting the filter from filter Panel.",
"RowReorderIcon": "Icon displayed on the <code>reorder</code> column type to reorder a row.",
"QuickFilterIcon": "Icon displayed on the quick filter input."
"QuickFilterIcon": "Icon displayed on the quick filter input.",
"QuickFilterClearIcon": "Icon displayed on the quick filter reset input."
}
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
import * as React from 'react';
import PropTypes from 'prop-types';
import TextField, { TextFieldProps } from '@mui/material/TextField';
import IconButton from '@mui/material/IconButton';
import { styled } from '@mui/material/styles';
import { debounce } from '@mui/material/utils';
import { useGridApiContext } from '../../hooks/utils/useGridApiContext';
import { useGridRootProps } from '../../hooks/utils/useGridRootProps';
import { useGridSelector } from '../../hooks/utils/useGridSelector';
import { gridQuickFilterValuesSelector } from '../../hooks/features/filter';
import { GridFilterModel } from '../../models/gridFilterModel';
import { isDeepEqual } from '../../utils/utils';

const GridToolbarQuickFilterRoot = styled(TextField, {
name: 'MuiDataGrid',
Expand All @@ -13,24 +18,46 @@ const GridToolbarQuickFilterRoot = styled(TextField, {
})(({ theme }) => ({
width: 'auto',
paddingBottom: theme.spacing(0.5),
'& .MuiSvgIcon-root': {
marginRight: theme.spacing(0.5),
'& input': {
marginLeft: theme.spacing(0.5),
},
'& .MuiInput-underline:before': {
borderBottom: `1px solid ${theme.palette.divider}`,
},
[`& input[type=search]::-ms-clear,
& input[type=search]::-ms-reveal`]: {
/* clears the 'X' icon from IE */
display: 'none',
width: 0,
height: 0,
},
[`& input[type="search"]::-webkit-search-decoration,
& input[type="search"]::-webkit-search-cancel-button,
& input[type="search"]::-webkit-search-results-button,
& input[type="search"]::-webkit-search-results-decoration`]: {
/* clears the 'X' icon from Chrome */
display: 'none',
},
}));

const defaultSearchValueParser = (searchText: string) =>
searchText.split(' ').filter((word) => word !== '');

const defaultSearchValueFormatter = (values: string[]) => values.join(' ');

export type GridToolbarQuickFilterProps = TextFieldProps & {
/**
* Function responsible for parsing text input in an array of independent values for quick filtering.
* @param {string} input The value entered by the user
* @returns {any[]} The array of value on which quick filter is applied
*/
quickFilterParser?: (input: string) => any[];
/**
* Function responsible for formatting values of quick filter in a string when the model is modified
* @param {any[]} values The new values passed to the quick filter model
* @returns {string} The string to display in the text field
*/
quickFilterFormatter?: (values: GridFilterModel['quickFilterValues']) => string;
/**
* The debounce time in milliseconds.
* @default 500
Expand All @@ -39,12 +66,36 @@ export type GridToolbarQuickFilterProps = TextFieldProps & {
};

function GridToolbarQuickFilter(props: GridToolbarQuickFilterProps) {
const { quickFilterParser = defaultSearchValueParser, debounceMs = 500, ...other } = props;
const {
quickFilterParser = defaultSearchValueParser,
quickFilterFormatter = defaultSearchValueFormatter,
debounceMs = 500,
...other
} = props;

const apiRef = useGridApiContext();
const rootProps = useGridRootProps();
const quickFilterValues = useGridSelector(apiRef, gridQuickFilterValuesSelector);

const [searchValue, setSearchValue] = React.useState(() =>
quickFilterFormatter(quickFilterValues ?? []),
);

const [prevQuickFilterValues, setPrevQuickFilterValues] = React.useState(quickFilterValues);

React.useEffect(() => {
if (!isDeepEqual(prevQuickFilterValues, quickFilterValues)) {
// The model of quick filter value has been updated
setPrevQuickFilterValues(quickFilterValues);

const [searchValue, setSearchValue] = React.useState('');
// Update the input value if needed to match the new model
setSearchValue((prevSearchValue) =>
isDeepEqual(quickFilterParser(prevSearchValue), quickFilterValues)
? prevSearchValue
: quickFilterFormatter(quickFilterValues ?? []),
);
}
}, [prevQuickFilterValues, quickFilterValues, quickFilterFormatter, quickFilterParser]);

const updateSearchValue = React.useCallback(
(newSearchValue) => {
Expand All @@ -67,6 +118,11 @@ function GridToolbarQuickFilter(props: GridToolbarQuickFilterProps) {
[debouncedUpdateSearchValue],
);

const handleSearchReset = React.useCallback(() => {
setSearchValue('');
updateSearchValue('');
}, [updateSearchValue]);

return (
<GridToolbarQuickFilterRoot
as={rootProps.components.BaseTextField}
Expand All @@ -78,6 +134,16 @@ function GridToolbarQuickFilter(props: GridToolbarQuickFilterProps) {
type="search"
InputProps={{
startAdornment: <rootProps.components.QuickFilterIcon fontSize="small" />,
endAdornment: (
<IconButton
aria-label={apiRef.current.getLocaleText('toolbarQuickFilterDeleteIconLabel')}
size="small"
sx={{ visibility: searchValue ? 'visible' : 'hidden' }}
onClick={handleSearchReset}
>
<rootProps.components.QuickFilterClearIcon fontSize="small" />
</IconButton>
),
}}
{...other}
{...rootProps.componentsProps?.baseTextField}
Expand All @@ -95,6 +161,12 @@ GridToolbarQuickFilter.propTypes = {
* @default 500
*/
debounceMs: PropTypes.number,
/**
* Function responsible for formatting values of quick filter in a string when the model is modified
* @param {any[]} values The new values passed to the quick filter model
* @returns {string} The string to display in the text field
*/
quickFilterFormatter: PropTypes.func,
/**
* Function responsible for parsing text input in an array of independent values for quick filtering.
* @param {string} input The value entered by the user
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ const DEFAULT_GRID_ICON_SLOTS_COMPONENTS: GridIconSlotsComponent = {
DetailPanelCollapseIcon: GridRemoveIcon,
RowReorderIcon: GridDragIcon,
QuickFilterIcon: GridSearchIcon,
QuickFilterClearIcon: GridCloseIcon,
};

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,15 @@ export const gridFilterModelSelector = createSelector(
(filterState) => filterState.filterModel,
);

/**
* Get the current quick filter values.
* @category Filtering
*/
export const gridQuickFilterValuesSelector = createSelector(
gridFilterModelSelector,
(filterModel) => filterModel.quickFilterValues,
);

/**
* @category Filtering
* @ignore - do not document.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -125,4 +125,9 @@ export interface GridIconSlotsComponent {
* @default GridSearchIcon
*/
QuickFilterIcon: React.JSXElementConstructor<any>;
/**
* Icon displayed on the quick filter reset input.
* @default GridCloseIcon
*/
QuickFilterClearIcon: React.JSXElementConstructor<any>;
}
Loading

0 comments on commit 23aaa6b

Please sign in to comment.