mirror of
https://github.com/prowler-cloud/prowler.git
synced 2026-10-11 05:54:17 +00:00
fix(ui): scope Enter-only search to controlled mode, restore uncontrolled debounce
This commit is contained in:
2 files changed
+45
-36
No files matched your search
@@ -171,7 +171,7 @@ export function FindingsGroupTable({
|
||||
return (
|
||||
<InlineResourceContainer
|
||||
ref={inlineRef}
|
||||
key={`${group.checkId}|${searchParams.toString()}`}
|
||||
key={`${group.checkId}|${searchParams.toString()}|${resourceSearch}`}
|
||||
group={expandedGroup}
|
||||
resourceSearch={resourceSearch}
|
||||
columnCount={columns.length}
|
||||
@@ -204,19 +204,7 @@ export function FindingsGroupTable({
|
||||
}
|
||||
controlledSearch={expandedCheckId ? resourceSearchInput : undefined}
|
||||
onSearchChange={expandedCheckId ? setResourceSearchInput : undefined}
|
||||
onSearchCommit={
|
||||
expandedCheckId
|
||||
? (value: string) => {
|
||||
setResourceSearch(value);
|
||||
// Trigger re-fetch with updated filters imperatively.
|
||||
// The hook uses refs (no auto-refetch on prop change), so
|
||||
// we call refresh() after the state update is committed.
|
||||
// queueMicrotask ensures the new resourceSearch is in the
|
||||
// filters object before refresh reads filtersRef.current.
|
||||
queueMicrotask(() => inlineRef.current?.refresh());
|
||||
}
|
||||
: undefined
|
||||
}
|
||||
onSearchCommit={expandedCheckId ? setResourceSearch : undefined}
|
||||
searchBadge={
|
||||
expandedGroup
|
||||
? { label: expandedGroup.checkTitle, onDismiss: handleCollapse }
|
||||
|
||||
@@ -84,29 +84,26 @@ export const DataTableSearch = ({
|
||||
if (isControlled) return;
|
||||
const searchFromUrl = searchParams.get(searchParam) || "";
|
||||
setInternalValue(searchFromUrl);
|
||||
// If there's a search value, start expanded
|
||||
if (searchFromUrl) {
|
||||
setIsExpanded(true);
|
||||
}
|
||||
}, [searchParams, searchParam, isControlled]);
|
||||
|
||||
// Handle input change with debounce
|
||||
const handleChange = (newValue: string) => {
|
||||
// For controlled mode, update display immediately, debounce the callback
|
||||
if (isControlled) {
|
||||
// Update display value immediately for responsive typing
|
||||
setDisplayValue(newValue);
|
||||
|
||||
if (debounceTimeoutRef.current) {
|
||||
clearTimeout(debounceTimeoutRef.current);
|
||||
}
|
||||
|
||||
// When onSearchCommit is provided, the actual search only fires on
|
||||
// Enter. Don't show the loading spinner on every keystroke — it
|
||||
// misleads the user into thinking a search is happening.
|
||||
if (!onSearchCommit) {
|
||||
setIsLoading(true);
|
||||
if (onSearchCommit) {
|
||||
// Enter-to-commit mode: sync parent immediately (no debounce, no loading).
|
||||
// The actual search commit happens on Enter via onSearchCommit.
|
||||
onSearchChange(newValue);
|
||||
return;
|
||||
}
|
||||
|
||||
// Standard controlled mode: debounce the callback
|
||||
setIsLoading(true);
|
||||
debounceTimeoutRef.current = setTimeout(() => {
|
||||
onSearchChange(newValue);
|
||||
setIsLoading(false);
|
||||
@@ -114,9 +111,38 @@ export const DataTableSearch = ({
|
||||
return;
|
||||
}
|
||||
|
||||
// Uncontrolled mode: only update the display value on keystroke.
|
||||
// The actual URL update happens on Enter (see onKeyDown handler).
|
||||
// Uncontrolled mode: update display + debounce URL update
|
||||
setInternalValue(newValue);
|
||||
|
||||
if (debounceTimeoutRef.current) {
|
||||
clearTimeout(debounceTimeoutRef.current);
|
||||
}
|
||||
|
||||
if (paramPrefix) {
|
||||
setIsLoading(true);
|
||||
debounceTimeoutRef.current = setTimeout(() => {
|
||||
const params = new URLSearchParams(searchParams.toString());
|
||||
if (newValue) {
|
||||
params.set(searchParam, newValue);
|
||||
} else {
|
||||
params.delete(searchParam);
|
||||
}
|
||||
params.set(pageParam, "1");
|
||||
router.push(`${pathname}?${params.toString()}`, { scroll: false });
|
||||
setIsLoading(false);
|
||||
}, SEARCH_DEBOUNCE_MS);
|
||||
} else {
|
||||
if (newValue) {
|
||||
setIsLoading(true);
|
||||
debounceTimeoutRef.current = setTimeout(() => {
|
||||
updateFilter("search", newValue);
|
||||
setIsLoading(false);
|
||||
}, SEARCH_DEBOUNCE_MS);
|
||||
} else {
|
||||
setIsLoading(false);
|
||||
updateFilter("search", null);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Cleanup timeout on unmount
|
||||
@@ -189,25 +215,21 @@ export const DataTableSearch = ({
|
||||
onKeyDown={(e) => {
|
||||
if (e.key !== "Enter") return;
|
||||
|
||||
// Cancel any pending debounce
|
||||
// Cancel any pending debounce — Enter commits immediately
|
||||
if (debounceTimeoutRef.current) {
|
||||
clearTimeout(debounceTimeoutRef.current);
|
||||
debounceTimeoutRef.current = null;
|
||||
}
|
||||
setIsLoading(false);
|
||||
|
||||
// Controlled mode with explicit commit callback
|
||||
if (onSearchCommit) {
|
||||
if (onSearchChange) {
|
||||
onSearchChange(value);
|
||||
}
|
||||
if (isControlled && onSearchCommit) {
|
||||
onSearchCommit(value);
|
||||
setIsLoading(false);
|
||||
return;
|
||||
}
|
||||
|
||||
// Uncontrolled mode: commit search to URL on Enter
|
||||
// Uncontrolled mode: immediate URL update (shortcut for debounce)
|
||||
if (!isControlled) {
|
||||
setIsLoading(true);
|
||||
if (paramPrefix) {
|
||||
const params = new URLSearchParams(searchParams.toString());
|
||||
if (value) {
|
||||
@@ -222,7 +244,6 @@ export const DataTableSearch = ({
|
||||
} else {
|
||||
updateFilter("search", value || null);
|
||||
}
|
||||
setIsLoading(false);
|
||||
}
|
||||
}}
|
||||
onFocus={handleFocus}
|
||||
|
||||
Reference in new issue
Block a user