diff --git a/web/packages/common/src/components/DataView/FilterPanel/TextFilter.tsx b/web/packages/common/src/components/DataView/FilterPanel/TextFilter.tsx index 3efea4fd5e..e2138b52f1 100644 --- a/web/packages/common/src/components/DataView/FilterPanel/TextFilter.tsx +++ b/web/packages/common/src/components/DataView/FilterPanel/TextFilter.tsx @@ -2,20 +2,39 @@ // SPDX-License-Identifier: Apache-2.0 import type { DataViewColumn } from '@nemo/common/src/components/DataView/FilterPanel/types'; +import { DEFAULT_DEBOUNCE_MS } from '@nemo/common/src/constants'; import { TextInput } from '@nvidia/foundations-react-core'; +import { useEffect, useState } from 'react'; +import { useDebouncedCallback } from 'use-debounce'; export function TextFilterControl({ column }: { column: DataViewColumn }) { const filter = column.columnDef.meta?.filter; - const value = column.getFilterValue() as string | undefined; + const committedValue = column.getFilterValue() as string | undefined; + const [localValue, setLocalValue] = useState(committedValue ?? ''); + + // Keep local state in sync when the committed filter value changes externally + // (e.g. "Clear Filters" resets it to undefined). + useEffect(() => { + setLocalValue(committedValue ?? ''); + }, [committedValue]); + + const commitFilter = useDebouncedCallback((v: string) => { + column.setFilterValue(v || undefined); + }, DEFAULT_DEBOUNCE_MS); + + const handleChange = (v: string) => { + setLocalValue(v); + commitFilter(v); + }; return ( column.setFilterValue(v || undefined)} + value={localValue} + onValueChange={handleChange} dismissible - size="small" + size="medium" /> ); } diff --git a/web/packages/common/src/constants/index.ts b/web/packages/common/src/constants/index.ts index 42fc053cb3..443e26c921 100644 --- a/web/packages/common/src/constants/index.ts +++ b/web/packages/common/src/constants/index.ts @@ -1,6 +1,7 @@ // SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. // SPDX-License-Identifier: Apache-2.0 +export const DEFAULT_DEBOUNCE_MS = 300; export const DEFAULT_NAMESPACE = 'default'; export const DEFAULT_MODEL_NAME = 'meta/llama-3.1-8b-instruct'; export const JOB_POLLING_INTERVAL_MS = 5000; diff --git a/web/packages/common/src/hooks/useStudioDataViewState/index.ts b/web/packages/common/src/hooks/useStudioDataViewState/index.ts index 544a47cd9a..dbe8cd60da 100644 --- a/web/packages/common/src/hooks/useStudioDataViewState/index.ts +++ b/web/packages/common/src/hooks/useStudioDataViewState/index.ts @@ -1,6 +1,7 @@ // SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. // SPDX-License-Identifier: Apache-2.0 +import { DEFAULT_DEBOUNCE_MS } from '@nemo/common/src/constants'; import { useCallback, useEffect, useMemo, useRef } from 'react'; import { useSearchParams } from 'react-router-dom'; import { useDebounce } from 'use-debounce'; @@ -384,8 +385,11 @@ export const useStudioDataViewState = >( }, [dataViewState.sorting, dataViewState.pagination, urlSorting, updateUrlParams]); // Debounced search bar and column filters for API queries - const [debouncedSearchBar] = useDebounce(dataViewState.searchBar.state, 300); - const [debouncedColumnFilters] = useDebounce(dataViewState.columnFiltering.state, 300); + const [debouncedSearchBar] = useDebounce(dataViewState.searchBar.state, DEFAULT_DEBOUNCE_MS); + const [debouncedColumnFilters] = useDebounce( + dataViewState.columnFiltering.state, + DEFAULT_DEBOUNCE_MS + ); // Track previous debounced values to detect changes for pagination reset const prevDebouncedSearchRef = useRef(debouncedSearchBar); diff --git a/web/packages/common/src/hooks/useTableFilters/index.ts b/web/packages/common/src/hooks/useTableFilters/index.ts index 438b20e59a..dcfc8eab6c 100644 --- a/web/packages/common/src/hooks/useTableFilters/index.ts +++ b/web/packages/common/src/hooks/useTableFilters/index.ts @@ -1,6 +1,7 @@ // SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. // SPDX-License-Identifier: Apache-2.0 +import { DEFAULT_DEBOUNCE_MS } from '@nemo/common/src/constants'; import { useState, useEffect, useCallback, SetStateAction, Dispatch } from 'react'; import { useSearchParams } from 'react-router-dom'; import { useDebounce } from 'use-debounce'; @@ -210,8 +211,8 @@ export function useTableFilters({ return ''; }); - const [debouncedFilterState] = useDebounce(filterState, 300); - const [debouncedTextSearch] = useDebounce(textSearch, 300); + const [debouncedFilterState] = useDebounce(filterState, DEFAULT_DEBOUNCE_MS); + const [debouncedTextSearch] = useDebounce(textSearch, DEFAULT_DEBOUNCE_MS); // Reset filters function const resetFilters = useCallback(() => {