From a1f6d7e89e6b826eb7822615dec424ab71393773 Mon Sep 17 00:00:00 2001 From: Rom Grk Date: Mon, 14 Aug 2023 11:49:19 -0400 Subject: [PATCH] [DataGrid] Lower filter debounce delay (#9712) --- .../data-grid/demo/PopularFeaturesDemo.js | 2 +- .../data-grid/demo/PopularFeaturesDemo.tsx | 2 +- .../filtering/CustomMultiValueOperator.js | 7 ++-- .../filtering/CustomMultiValueOperator.tsx | 6 ++-- .../data-grid/filtering/QuickFilteringGrid.js | 1 - .../filtering/QuickFilteringGrid.tsx | 1 - .../filtering/QuickFilteringGrid.tsx.preview | 1 - .../filtering/QuickFilteringInitialize.js | 1 - .../filtering/QuickFilteringInitialize.tsx | 1 - .../x/api/data-grid/data-grid-premium.json | 1 + docs/pages/x/api/data-grid/data-grid-pro.json | 1 + docs/pages/x/api/data-grid/data-grid.json | 1 + .../data-grid/grid-toolbar-quick-filter.json | 2 +- .../api-docs/data-grid/data-grid-premium.json | 5 +++ .../api-docs/data-grid/data-grid-pro.json | 5 +++ .../api-docs/data-grid/data-grid.json | 5 +++ package.json | 2 +- .../src/DataGridPremium/DataGrid.tsx | 2 ++ .../src/DataGridPremium/DataGridPremium.tsx | 5 +++ .../src/DataGridPro/DataGrid.tsx | 2 ++ .../src/DataGridPro/DataGridPro.tsx | 5 +++ .../src/tests/filtering.DataGridPro.test.tsx | 4 ++- .../x-data-grid/src/DataGrid/DataGrid.tsx | 19 ++++++++++- .../src/DataGrid/useDataGridProps.ts | 1 + .../panel/filterPanel/GridFilterInputDate.tsx | 18 +++------- .../filterPanel/GridFilterInputValue.tsx | 32 ++++++++--------- .../panel/filterPanel/GridFilterPanel.tsx | 7 +--- .../toolbar/GridToolbarQuickFilter.tsx | 14 ++++---- .../x-data-grid/src/hooks/utils/useTimeout.ts | 34 +++++++++++++++++++ .../src/models/props/DataGridProps.ts | 5 +++ 30 files changed, 131 insertions(+), 61 deletions(-) create mode 100644 packages/grid/x-data-grid/src/hooks/utils/useTimeout.ts diff --git a/docs/data/data-grid/demo/PopularFeaturesDemo.js b/docs/data/data-grid/demo/PopularFeaturesDemo.js index 5934c43a7132e..8374403bef89f 100644 --- a/docs/data/data-grid/demo/PopularFeaturesDemo.js +++ b/docs/data/data-grid/demo/PopularFeaturesDemo.js @@ -412,7 +412,7 @@ export default function PopularFeaturesDemo() { detailPanelCollapseIcon: ArrowUp, }} slotProps={{ - toolbar: { showQuickFilter: true, quickFilterProps: { debounceMs: 500 } }, + toolbar: { showQuickFilter: true }, }} getDetailPanelContent={getDetailPanelContent} getDetailPanelHeight={getDetailPanelHeight} diff --git a/docs/data/data-grid/demo/PopularFeaturesDemo.tsx b/docs/data/data-grid/demo/PopularFeaturesDemo.tsx index e637e47b7025e..4e614cc31a790 100644 --- a/docs/data/data-grid/demo/PopularFeaturesDemo.tsx +++ b/docs/data/data-grid/demo/PopularFeaturesDemo.tsx @@ -432,7 +432,7 @@ export default function PopularFeaturesDemo() { detailPanelCollapseIcon: ArrowUp, }} slotProps={{ - toolbar: { showQuickFilter: true, quickFilterProps: { debounceMs: 500 } }, + toolbar: { showQuickFilter: true }, }} getDetailPanelContent={getDetailPanelContent} getDetailPanelHeight={getDetailPanelHeight} diff --git a/docs/data/data-grid/filtering/CustomMultiValueOperator.js b/docs/data/data-grid/filtering/CustomMultiValueOperator.js index 8a339e582458c..75e3549d6a239 100644 --- a/docs/data/data-grid/filtering/CustomMultiValueOperator.js +++ b/docs/data/data-grid/filtering/CustomMultiValueOperator.js @@ -1,13 +1,12 @@ import * as React from 'react'; import Box from '@mui/material/Box'; import TextField from '@mui/material/TextField'; -import { DataGrid } from '@mui/x-data-grid'; +import { DataGrid, useGridRootProps } from '@mui/x-data-grid'; import { useDemoData } from '@mui/x-data-grid-generator'; import SyncIcon from '@mui/icons-material/Sync'; -const SUBMIT_FILTER_STROKE_TIME = 500; - function InputNumberInterval(props) { + const rootProps = useGridRootProps(); const { item, applyValue, focusElementRef = null } = props; const filterTimeout = React.useRef(); @@ -33,7 +32,7 @@ function InputNumberInterval(props) { filterTimeout.current = setTimeout(() => { setIsApplying(false); applyValue({ ...item, value: [lowerBound, upperBound] }); - }, SUBMIT_FILTER_STROKE_TIME); + }, rootProps.filterDebounceMs); }; const handleUpperFilterChange = (event) => { diff --git a/docs/data/data-grid/filtering/CustomMultiValueOperator.tsx b/docs/data/data-grid/filtering/CustomMultiValueOperator.tsx index c69e5ae6d3851..2532d64d3c881 100644 --- a/docs/data/data-grid/filtering/CustomMultiValueOperator.tsx +++ b/docs/data/data-grid/filtering/CustomMultiValueOperator.tsx @@ -7,13 +7,13 @@ import { GridFilterItem, GridFilterModel, GridFilterOperator, + useGridRootProps, } from '@mui/x-data-grid'; import { useDemoData } from '@mui/x-data-grid-generator'; import SyncIcon from '@mui/icons-material/Sync'; -const SUBMIT_FILTER_STROKE_TIME = 500; - function InputNumberInterval(props: GridFilterInputValueProps) { + const rootProps = useGridRootProps(); const { item, applyValue, focusElementRef = null } = props; const filterTimeout = React.useRef(); @@ -41,7 +41,7 @@ function InputNumberInterval(props: GridFilterInputValueProps) { filterTimeout.current = setTimeout(() => { setIsApplying(false); applyValue({ ...item, value: [lowerBound, upperBound] }); - }, SUBMIT_FILTER_STROKE_TIME); + }, rootProps.filterDebounceMs); }; const handleUpperFilterChange: TextFieldProps['onChange'] = (event) => { diff --git a/docs/data/data-grid/filtering/QuickFilteringGrid.js b/docs/data/data-grid/filtering/QuickFilteringGrid.js index 3c7f54ba42468..c55663c35e259 100644 --- a/docs/data/data-grid/filtering/QuickFilteringGrid.js +++ b/docs/data/data-grid/filtering/QuickFilteringGrid.js @@ -30,7 +30,6 @@ export default function QuickFilteringGrid() { slotProps={{ toolbar: { showQuickFilter: true, - quickFilterProps: { debounceMs: 500 }, }, }} /> diff --git a/docs/data/data-grid/filtering/QuickFilteringGrid.tsx b/docs/data/data-grid/filtering/QuickFilteringGrid.tsx index 3c7f54ba42468..c55663c35e259 100644 --- a/docs/data/data-grid/filtering/QuickFilteringGrid.tsx +++ b/docs/data/data-grid/filtering/QuickFilteringGrid.tsx @@ -30,7 +30,6 @@ export default function QuickFilteringGrid() { slotProps={{ toolbar: { showQuickFilter: true, - quickFilterProps: { debounceMs: 500 }, }, }} /> diff --git a/docs/data/data-grid/filtering/QuickFilteringGrid.tsx.preview b/docs/data/data-grid/filtering/QuickFilteringGrid.tsx.preview index fa5d8058a574d..3c3e9101c1d6b 100644 --- a/docs/data/data-grid/filtering/QuickFilteringGrid.tsx.preview +++ b/docs/data/data-grid/filtering/QuickFilteringGrid.tsx.preview @@ -8,7 +8,6 @@ slotProps={{ toolbar: { showQuickFilter: true, - quickFilterProps: { debounceMs: 500 }, }, }} /> \ No newline at end of file diff --git a/docs/data/data-grid/filtering/QuickFilteringInitialize.js b/docs/data/data-grid/filtering/QuickFilteringInitialize.js index 5d6eb5e5fdf3f..d71f54958e77d 100644 --- a/docs/data/data-grid/filtering/QuickFilteringInitialize.js +++ b/docs/data/data-grid/filtering/QuickFilteringInitialize.js @@ -39,7 +39,6 @@ export default function QuickFilteringInitialize() { slotProps={{ toolbar: { showQuickFilter: true, - quickFilterProps: { debounceMs: 500 }, }, }} /> diff --git a/docs/data/data-grid/filtering/QuickFilteringInitialize.tsx b/docs/data/data-grid/filtering/QuickFilteringInitialize.tsx index 5d6eb5e5fdf3f..d71f54958e77d 100644 --- a/docs/data/data-grid/filtering/QuickFilteringInitialize.tsx +++ b/docs/data/data-grid/filtering/QuickFilteringInitialize.tsx @@ -39,7 +39,6 @@ export default function QuickFilteringInitialize() { slotProps={{ toolbar: { showQuickFilter: true, - quickFilterProps: { debounceMs: 500 }, }, }} /> diff --git a/docs/pages/x/api/data-grid/data-grid-premium.json b/docs/pages/x/api/data-grid/data-grid-premium.json index 1c531ac1b5706..90b3ed8ba9769 100644 --- a/docs/pages/x/api/data-grid/data-grid-premium.json +++ b/docs/pages/x/api/data-grid/data-grid-premium.json @@ -82,6 +82,7 @@ "description": "{ ariaV7?: bool, clipboardPaste?: bool, columnGrouping?: bool, lazyLoading?: bool, warnIfFocusStateIsNotSynced?: bool }" } }, + "filterDebounceMs": { "type": { "name": "number" }, "default": "150" }, "filterMode": { "type": { "name": "custom", "description": "'client'
| 'server'" }, "default": "\"client\"" diff --git a/docs/pages/x/api/data-grid/data-grid-pro.json b/docs/pages/x/api/data-grid/data-grid-pro.json index 582ce15c14e98..dde6b87494073 100644 --- a/docs/pages/x/api/data-grid/data-grid-pro.json +++ b/docs/pages/x/api/data-grid/data-grid-pro.json @@ -70,6 +70,7 @@ "description": "{ ariaV7?: bool, columnGrouping?: bool, lazyLoading?: bool, warnIfFocusStateIsNotSynced?: bool }" } }, + "filterDebounceMs": { "type": { "name": "number" }, "default": "150" }, "filterMode": { "type": { "name": "custom", "description": "'client'
| 'server'" }, "default": "\"client\"" diff --git a/docs/pages/x/api/data-grid/data-grid.json b/docs/pages/x/api/data-grid/data-grid.json index 4c447cca2e0ef..5857f2660004c 100644 --- a/docs/pages/x/api/data-grid/data-grid.json +++ b/docs/pages/x/api/data-grid/data-grid.json @@ -51,6 +51,7 @@ "description": "{ ariaV7?: bool, columnGrouping?: bool, warnIfFocusStateIsNotSynced?: bool }" } }, + "filterDebounceMs": { "type": { "name": "number" }, "default": "150" }, "filterMode": { "type": { "name": "enum", "description": "'client'
| 'server'" }, "default": "\"client\"" diff --git a/docs/pages/x/api/data-grid/grid-toolbar-quick-filter.json b/docs/pages/x/api/data-grid/grid-toolbar-quick-filter.json index f3490fa956ca7..3ee5f331df7fb 100644 --- a/docs/pages/x/api/data-grid/grid-toolbar-quick-filter.json +++ b/docs/pages/x/api/data-grid/grid-toolbar-quick-filter.json @@ -1,6 +1,6 @@ { "props": { - "debounceMs": { "type": { "name": "number" }, "default": "500" }, + "debounceMs": { "type": { "name": "number" }, "default": "150" }, "quickFilterFormatter": { "type": { "name": "func" }, "signature": { diff --git a/docs/translations/api-docs/data-grid/data-grid-premium.json b/docs/translations/api-docs/data-grid/data-grid-premium.json index 17eefbda0d722..afdcc1d9a0e9e 100644 --- a/docs/translations/api-docs/data-grid/data-grid-premium.json +++ b/docs/translations/api-docs/data-grid/data-grid-premium.json @@ -211,6 +211,11 @@ "deprecated": "", "typeDescriptions": {} }, + "filterDebounceMs": { + "description": "The milliseconds delay to wait after a keystroke before triggering filtering.", + "deprecated": "", + "typeDescriptions": {} + }, "filterMode": { "description": "Filtering can be processed on the server or client-side. Set it to 'server' if you would like to handle filtering on the server-side.", "deprecated": "", diff --git a/docs/translations/api-docs/data-grid/data-grid-pro.json b/docs/translations/api-docs/data-grid/data-grid-pro.json index df19e9961d411..ab350364a349f 100644 --- a/docs/translations/api-docs/data-grid/data-grid-pro.json +++ b/docs/translations/api-docs/data-grid/data-grid-pro.json @@ -181,6 +181,11 @@ "deprecated": "", "typeDescriptions": {} }, + "filterDebounceMs": { + "description": "The milliseconds delay to wait after a keystroke before triggering filtering.", + "deprecated": "", + "typeDescriptions": {} + }, "filterMode": { "description": "Filtering can be processed on the server or client-side. Set it to 'server' if you would like to handle filtering on the server-side.", "deprecated": "", diff --git a/docs/translations/api-docs/data-grid/data-grid.json b/docs/translations/api-docs/data-grid/data-grid.json index cf037e3b1c51f..ac8899850eb5e 100644 --- a/docs/translations/api-docs/data-grid/data-grid.json +++ b/docs/translations/api-docs/data-grid/data-grid.json @@ -126,6 +126,11 @@ "deprecated": "", "typeDescriptions": {} }, + "filterDebounceMs": { + "description": "The milliseconds delay to wait after a keystroke before triggering filtering.", + "deprecated": "", + "typeDescriptions": {} + }, "filterMode": { "description": "Filtering can be processed on the server or client-side. Set it to 'server' if you would like to handle filtering on the server-side.", "deprecated": "", diff --git a/package.json b/package.json index c331842dd3dc8..7367a716913ae 100644 --- a/package.json +++ b/package.json @@ -64,7 +64,7 @@ "release:publish": "lerna publish from-package --no-private --dist-tag latest --contents build", "release:publish:dry-run": "lerna publish from-package --dist-tag latest --contents build --registry=\"http://localhost:4873/\"", "release:tag": "node scripts/releaseTag.mjs", - "validate": "concurrently \"yarn prettier && yarn eslint\" \"yarn docs:typescript:formatted\" \"yarn docs:api\"" + "validate": "concurrently \"yarn prettier && yarn eslint\" \"yarn proptypes\" \"yarn docs:typescript:formatted\" \"yarn docs:api\"" }, "devDependencies": { "@argos-ci/core": "^0.9.0", diff --git a/packages/grid/x-data-grid-premium/src/DataGridPremium/DataGrid.tsx b/packages/grid/x-data-grid-premium/src/DataGridPremium/DataGrid.tsx index d65e73b9fc43b..e8fa734546092 100644 --- a/packages/grid/x-data-grid-premium/src/DataGridPremium/DataGrid.tsx +++ b/packages/grid/x-data-grid-premium/src/DataGridPremium/DataGrid.tsx @@ -1,3 +1,5 @@ +export { SUBMIT_FILTER_STROKE_TIME, SUBMIT_FILTER_DATE_STROKE_TIME } from '@mui/x-data-grid'; + /** * @deprecated Import DataGridPremium instead. */ diff --git a/packages/grid/x-data-grid-premium/src/DataGridPremium/DataGridPremium.tsx b/packages/grid/x-data-grid-premium/src/DataGridPremium/DataGridPremium.tsx index 96522e233302a..06ad924a6c972 100644 --- a/packages/grid/x-data-grid-premium/src/DataGridPremium/DataGridPremium.tsx +++ b/packages/grid/x-data-grid-premium/src/DataGridPremium/DataGridPremium.tsx @@ -291,6 +291,11 @@ DataGridPremiumRaw.propTypes = { lazyLoading: PropTypes.bool, warnIfFocusStateIsNotSynced: PropTypes.bool, }), + /** + * The milliseconds delay to wait after a keystroke before triggering filtering. + * @default 150 + */ + filterDebounceMs: PropTypes.number, /** * Filtering can be processed on the server or client-side. * Set it to 'server' if you would like to handle filtering on the server-side. diff --git a/packages/grid/x-data-grid-pro/src/DataGridPro/DataGrid.tsx b/packages/grid/x-data-grid-pro/src/DataGridPro/DataGrid.tsx index 0dca9c1f41c1f..59f48be65ccc8 100644 --- a/packages/grid/x-data-grid-pro/src/DataGridPro/DataGrid.tsx +++ b/packages/grid/x-data-grid-pro/src/DataGridPro/DataGrid.tsx @@ -1,3 +1,5 @@ +export { SUBMIT_FILTER_STROKE_TIME, SUBMIT_FILTER_DATE_STROKE_TIME } from '@mui/x-data-grid'; + /** * @deprecated Import DataGridPro instead. */ diff --git a/packages/grid/x-data-grid-pro/src/DataGridPro/DataGridPro.tsx b/packages/grid/x-data-grid-pro/src/DataGridPro/DataGridPro.tsx index 682e80b0ccefb..c98f167da2f55 100644 --- a/packages/grid/x-data-grid-pro/src/DataGridPro/DataGridPro.tsx +++ b/packages/grid/x-data-grid-pro/src/DataGridPro/DataGridPro.tsx @@ -258,6 +258,11 @@ DataGridProRaw.propTypes = { lazyLoading: PropTypes.bool, warnIfFocusStateIsNotSynced: PropTypes.bool, }), + /** + * The milliseconds delay to wait after a keystroke before triggering filtering. + * @default 150 + */ + filterDebounceMs: PropTypes.number, /** * Filtering can be processed on the server or client-side. * Set it to 'server' if you would like to handle filtering on the server-side. diff --git a/packages/grid/x-data-grid-pro/src/tests/filtering.DataGridPro.test.tsx b/packages/grid/x-data-grid-pro/src/tests/filtering.DataGridPro.test.tsx index eb319fc3b2958..6922d091c3099 100644 --- a/packages/grid/x-data-grid-pro/src/tests/filtering.DataGridPro.test.tsx +++ b/packages/grid/x-data-grid-pro/src/tests/filtering.DataGridPro.test.tsx @@ -6,7 +6,7 @@ import { GridLogicOperator, GridPreferencePanelsValue, GridRowModel, - SUBMIT_FILTER_STROKE_TIME, + DATA_GRID_PRO_PROPS_DEFAULT_VALUES, useGridApiRef, DataGridPro, GetColumnForNewFilterArgs, @@ -21,6 +21,8 @@ import * as React from 'react'; import { spy } from 'sinon'; import { getColumnHeaderCell, getColumnValues } from 'test/utils/helperFn'; +const SUBMIT_FILTER_STROKE_TIME = DATA_GRID_PRO_PROPS_DEFAULT_VALUES.filterDebounceMs; + const isJSDOM = /jsdom/.test(window.navigator.userAgent); describe(' - Filter', () => { diff --git a/packages/grid/x-data-grid/src/DataGrid/DataGrid.tsx b/packages/grid/x-data-grid/src/DataGrid/DataGrid.tsx index 84977452a9787..afd746f9581fb 100644 --- a/packages/grid/x-data-grid/src/DataGrid/DataGrid.tsx +++ b/packages/grid/x-data-grid/src/DataGrid/DataGrid.tsx @@ -5,7 +5,7 @@ import { GridBody, GridFooterPlaceholder, GridHeader, GridRoot } from '../compon import { DataGridProps } from '../models/props/DataGridProps'; import { GridContextProvider } from '../context/GridContextProvider'; import { useDataGridComponent } from './useDataGridComponent'; -import { useDataGridProps } from './useDataGridProps'; +import { useDataGridProps, DATA_GRID_PROPS_DEFAULT_VALUES } from './useDataGridProps'; import { DataGridVirtualScroller } from '../components/DataGridVirtualScroller'; import { GridValidRowModel } from '../models/gridRows'; @@ -42,6 +42,18 @@ interface DataGridComponent { export const DataGrid = React.memo(DataGridRaw) as DataGridComponent; +/** + * Remove at v7 + * @deprecated + */ +export const SUBMIT_FILTER_STROKE_TIME = DATA_GRID_PROPS_DEFAULT_VALUES.filterDebounceMs; + +/** + * Remove at v7 + * @deprecated + */ +export const SUBMIT_FILTER_DATE_STROKE_TIME = DATA_GRID_PROPS_DEFAULT_VALUES.filterDebounceMs; + DataGridRaw.propTypes = { // ----------------------------- Warning -------------------------------- // | These PropTypes are generated from the TypeScript type definitions | @@ -186,6 +198,11 @@ DataGridRaw.propTypes = { columnGrouping: PropTypes.bool, warnIfFocusStateIsNotSynced: PropTypes.bool, }), + /** + * The milliseconds delay to wait after a keystroke before triggering filtering. + * @default 150 + */ + filterDebounceMs: PropTypes.number, /** * Filtering can be processed on the server or client-side. * Set it to 'server' if you would like to handle filtering on the server-side. diff --git a/packages/grid/x-data-grid/src/DataGrid/useDataGridProps.ts b/packages/grid/x-data-grid/src/DataGrid/useDataGridProps.ts index 5c91de13e6611..79454bb420b28 100644 --- a/packages/grid/x-data-grid/src/DataGrid/useDataGridProps.ts +++ b/packages/grid/x-data-grid/src/DataGrid/useDataGridProps.ts @@ -55,6 +55,7 @@ export const DATA_GRID_PROPS_DEFAULT_VALUES: DataGridPropsWithDefaultValues = { disableVirtualization: false, editMode: GridEditModes.Cell, filterMode: 'client', + filterDebounceMs: 150, columnHeaderHeight: 56, hideFooter: false, hideFooterPagination: false, diff --git a/packages/grid/x-data-grid/src/components/panel/filterPanel/GridFilterInputDate.tsx b/packages/grid/x-data-grid/src/components/panel/filterPanel/GridFilterInputDate.tsx index e223652625c08..7d3162acb40f6 100644 --- a/packages/grid/x-data-grid/src/components/panel/filterPanel/GridFilterInputDate.tsx +++ b/packages/grid/x-data-grid/src/components/panel/filterPanel/GridFilterInputDate.tsx @@ -2,6 +2,7 @@ import * as React from 'react'; import PropTypes from 'prop-types'; import { TextFieldProps } from '@mui/material/TextField'; import { unstable_useId as useId } from '@mui/utils'; +import { useTimeout } from '../../../hooks/utils/useTimeout'; import { GridFilterInputValueProps } from './GridFilterInputValueProps'; import { useGridRootProps } from '../../../hooks/utils/useGridRootProps'; @@ -16,8 +17,6 @@ export type GridFilterInputDateProps = GridFilterInputValueProps & isFilterActive?: boolean; }; -export const SUBMIT_FILTER_DATE_STROKE_TIME = 500; - function GridFilterInputDate(props: GridFilterInputDateProps) { const { item, @@ -32,7 +31,7 @@ function GridFilterInputDate(props: GridFilterInputDateProps) { disabled, ...other } = props; - const filterTimeout = React.useRef(); + const filterTimeout = useTimeout(); const [filterValueState, setFilterValueState] = React.useState(item.value ?? ''); const [applying, setIsApplying] = React.useState(false); const id = useId(); @@ -42,24 +41,17 @@ function GridFilterInputDate(props: GridFilterInputDateProps) { (event: React.ChangeEvent) => { const value = event.target.value; - clearTimeout(filterTimeout.current); setFilterValueState(String(value)); setIsApplying(true); - filterTimeout.current = setTimeout(() => { + filterTimeout.start(rootProps.filterDebounceMs, () => { applyValue({ ...item, value }); setIsApplying(false); - }, SUBMIT_FILTER_DATE_STROKE_TIME); + }); }, - [applyValue, item], + [applyValue, item, rootProps.filterDebounceMs, filterTimeout], ); - React.useEffect(() => { - return () => { - clearTimeout(filterTimeout.current); - }; - }, []); - React.useEffect(() => { const itemValue = item.value ?? ''; setFilterValueState(String(itemValue)); diff --git a/packages/grid/x-data-grid/src/components/panel/filterPanel/GridFilterInputValue.tsx b/packages/grid/x-data-grid/src/components/panel/filterPanel/GridFilterInputValue.tsx index 7689d0b06f26f..670bfc7fbc3fa 100644 --- a/packages/grid/x-data-grid/src/components/panel/filterPanel/GridFilterInputValue.tsx +++ b/packages/grid/x-data-grid/src/components/panel/filterPanel/GridFilterInputValue.tsx @@ -2,11 +2,11 @@ import * as React from 'react'; import PropTypes from 'prop-types'; import { TextFieldProps } from '@mui/material/TextField'; import { unstable_useId as useId } from '@mui/utils'; +import { useTimeout } from '../../../hooks/utils/useTimeout'; +import { GridFilterItem } from '../../../models/gridFilterItem'; import { GridFilterInputValueProps } from './GridFilterInputValueProps'; import { useGridRootProps } from '../../../hooks/utils/useGridRootProps'; -export const SUBMIT_FILTER_STROKE_TIME = 500; - export type GridTypeFilterInputValueProps = GridFilterInputValueProps & TextFieldProps & { type?: 'text' | 'number' | 'date' | 'datetime-local'; @@ -18,6 +18,8 @@ export type GridTypeFilterInputValueProps = GridFilterInputValueProps & isFilterActive?: boolean; }; +type ItemPlusTag = GridFilterItem & { fromInput?: string }; + function GridFilterInputValue(props: GridTypeFilterInputValueProps) { const { item, @@ -32,7 +34,7 @@ function GridFilterInputValue(props: GridTypeFilterInputValueProps) { InputProps, ...others } = props; - const filterTimeout = React.useRef(); + const filterTimeout = useTimeout(); const [filterValueState, setFilterValueState] = React.useState(item.value ?? ''); const [applying, setIsApplying] = React.useState(false); const id = useId(); @@ -41,28 +43,24 @@ function GridFilterInputValue(props: GridTypeFilterInputValueProps) { const onFilterChange = React.useCallback( (event: React.ChangeEvent) => { const { value } = event.target; - clearTimeout(filterTimeout.current); setFilterValueState(String(value)); setIsApplying(true); - filterTimeout.current = setTimeout(() => { - applyValue({ ...item, value }); + filterTimeout.start(rootProps.filterDebounceMs, () => { + const newItem = { ...item, value, fromInput: id! }; + applyValue(newItem); setIsApplying(false); - }, SUBMIT_FILTER_STROKE_TIME); + }); }, - [applyValue, item], + [id, applyValue, item, rootProps.filterDebounceMs, filterTimeout], ); React.useEffect(() => { - return () => { - clearTimeout(filterTimeout.current); - }; - }, []); - - React.useEffect(() => { - const itemValue = item.value ?? ''; - setFilterValueState(String(itemValue)); - }, [item.value]); + const itemPlusTag = item as ItemPlusTag; + if (itemPlusTag.fromInput !== id) { + setFilterValueState(String(item.value ?? '')); + } + }, [id, item]); return ( ( ...other } = props; - const applyFilter = React.useCallback( - (item: GridFilterItem) => { - apiRef.current.upsertFilterItem(item); - }, - [apiRef], - ); + const applyFilter = apiRef.current.upsertFilterItem; const applyFilterLogicOperator = React.useCallback( (operator: GridLogicOperator) => { diff --git a/packages/grid/x-data-grid/src/components/toolbar/GridToolbarQuickFilter.tsx b/packages/grid/x-data-grid/src/components/toolbar/GridToolbarQuickFilter.tsx index 70977a70b2469..7198f31cb265d 100644 --- a/packages/grid/x-data-grid/src/components/toolbar/GridToolbarQuickFilter.tsx +++ b/packages/grid/x-data-grid/src/components/toolbar/GridToolbarQuickFilter.tsx @@ -62,23 +62,23 @@ export type GridToolbarQuickFilterProps = TextFieldProps & { quickFilterFormatter?: (values: NonNullable) => string; /** * The debounce time in milliseconds. - * @default 500 + * @default 150 */ debounceMs?: number; }; function GridToolbarQuickFilter(props: GridToolbarQuickFilterProps) { + const apiRef = useGridApiContext(); + const rootProps = useGridRootProps(); + const quickFilterValues = useGridSelector(apiRef, gridQuickFilterValuesSelector); + const { quickFilterParser = defaultSearchValueParser, quickFilterFormatter = defaultSearchValueFormatter, - debounceMs = 500, + debounceMs = rootProps.filterDebounceMs, ...other } = props; - const apiRef = useGridApiContext(); - const rootProps = useGridRootProps(); - const quickFilterValues = useGridSelector(apiRef, gridQuickFilterValuesSelector); - const [searchValue, setSearchValue] = React.useState(() => quickFilterFormatter(quickFilterValues ?? []), ); @@ -165,7 +165,7 @@ GridToolbarQuickFilter.propTypes = { // ---------------------------------------------------------------------- /** * The debounce time in milliseconds. - * @default 500 + * @default 150 */ debounceMs: PropTypes.number, /** diff --git a/packages/grid/x-data-grid/src/hooks/utils/useTimeout.ts b/packages/grid/x-data-grid/src/hooks/utils/useTimeout.ts new file mode 100644 index 0000000000000..79412fc43c404 --- /dev/null +++ b/packages/grid/x-data-grid/src/hooks/utils/useTimeout.ts @@ -0,0 +1,34 @@ +import { useLazyRef } from './useLazyRef'; +import { useOnMount } from './useOnMount'; + +class Timeout { + static create() { + return new Timeout(); + } + + currentId: number = 0; + + start(delay: number, fn: Function) { + this.clear(); + this.currentId = setTimeout(fn, delay); + } + + clear = () => { + if (this.currentId !== 0) { + clearTimeout(this.currentId); + this.currentId = 0; + } + }; + + disposeEffect = () => { + return this.clear; + }; +} + +export function useTimeout() { + const timeout = useLazyRef(Timeout.create).current; + + useOnMount(timeout.disposeEffect); + + return timeout; +} diff --git a/packages/grid/x-data-grid/src/models/props/DataGridProps.ts b/packages/grid/x-data-grid/src/models/props/DataGridProps.ts index 06b38a21fac01..381a52984aec9 100644 --- a/packages/grid/x-data-grid/src/models/props/DataGridProps.ts +++ b/packages/grid/x-data-grid/src/models/props/DataGridProps.ts @@ -230,6 +230,11 @@ export interface DataGridPropsWithDefaultValues { * @default "client" */ filterMode: GridFeatureMode; + /** + * The milliseconds delay to wait after a keystroke before triggering filtering. + * @default 150 + */ + filterDebounceMs: number; /** * Sets the height in pixel of the column headers in the grid. * @default 56