fix: reset query in quick filters (#8528)

* fix: added fix for reset all

* chore: added fix for empty query

* fix: fixed value suggestion issues

* chore: minor pr review changes

* chore: pr review fixes

* chore: cleanup for debouncedFetchValueSuggestions
This commit is contained in:
Abhi kumar 2025-07-15 17:24:18 +05:30 committed by GitHub
parent e78ca467fc
commit 4da7d62b0c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 79 additions and 63 deletions

View File

@ -26,9 +26,9 @@ import {
queryOperatorSuggestions,
} from 'constants/antlrQueryConstants';
import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder';
import { isNull } from 'lodash-es';
import { debounce, isNull } from 'lodash-es';
import { TriangleAlert } from 'lucide-react';
import { useCallback, useEffect, useRef, useState } from 'react';
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import {
IDetailedError,
IQueryContext,
@ -42,6 +42,7 @@ import {
getCurrentValueIndexAtCursor,
getQueryContextAtCursor,
} from 'utils/queryContextUtils';
import { unquote } from 'utils/stringUtils';
import { queryExamples } from './constants';
@ -109,7 +110,6 @@ function QuerySearch({
useEffect(() => {
setQuery(queryData.filter?.expression || '');
handleQueryValidation(queryData.filter?.expression || '');
}, [queryData.filter?.expression]);
const [keySuggestions, setKeySuggestions] = useState<
@ -400,6 +400,11 @@ function QuerySearch({
[activeKey, dataSource, isLoadingSuggestions],
);
const debouncedFetchValueSuggestions = useMemo(
() => debounce(fetchValueSuggestions, 300),
[fetchValueSuggestions],
);
const handleUpdate = useCallback((viewUpdate: { view: EditorView }): void => {
if (!isMountedRef.current) return;
@ -465,15 +470,18 @@ function QuerySearch({
const handleBlur = (): void => {
handleQueryValidation(query);
setIsFocused(false);
if (editorRef.current) {
closeCompletion(editorRef.current);
}
};
useEffect(() => {
if (query) {
handleQueryValidation(query);
}
return (): void => {
if (debouncedFetchValueSuggestions) {
debouncedFetchValueSuggestions.cancel();
}
};
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
@ -656,27 +664,38 @@ function QuerySearch({
return null;
}
const searchText = word?.text.toLowerCase().trim() ?? '';
let searchText = '';
if (
queryContext.currentPair &&
queryContext.currentPair.valuesPosition &&
queryContext.currentPair.valueList
) {
const { valuesPosition, valueList } = queryContext.currentPair;
const idx = getCurrentValueIndexAtCursor(valuesPosition, cursorPos.ch);
searchText = isNull(idx)
? ''
: unquote(valueList[idx]).toLowerCase().trim();
}
options = (valueSuggestions || []).filter((option) =>
option.label.toLowerCase().includes(searchText),
);
if (
keyName &&
((options.length === 0 &&
(((options.length === 0 || searchText === '') &&
(!isCompleteValuesList || lastValueRef.current !== searchText) &&
!isFetchingCompleteValuesList) ||
keyName !== activeKey ||
isLoadingSuggestions) &&
!(isLoadingSuggestions && lastKeyRef.current === keyName)
) {
setTimeout(() => {
fetchValueSuggestions({
key: keyName,
searchText,
fetchingComplete: true,
});
}, 300);
debouncedFetchValueSuggestions({
key: keyName,
searchText,
fetchingComplete: true,
});
}
// For values in bracket list, just add quotes without enclosing in brackets
@ -846,7 +865,11 @@ function QuerySearch({
if (!keyName) {
return null;
}
const searchText = word?.text.toLowerCase().trim() ?? '';
let searchText = '';
if (queryContext.currentPair && queryContext.currentPair.value) {
searchText = unquote(queryContext.currentPair.value).toLowerCase().trim();
}
options = (valueSuggestions || []).filter((option) =>
option.label.toLowerCase().includes(searchText),
@ -855,7 +878,7 @@ function QuerySearch({
// Trigger fetch only if needed
if (
keyName &&
((options.length === 0 &&
(((options.length === 0 || searchText === '') &&
(!isCompleteValuesList || lastValueRef.current !== searchText) &&
!isFetchingCompleteValuesList) ||
keyName !== activeKey ||
@ -863,13 +886,11 @@ function QuerySearch({
!(isLoadingSuggestions && lastKeyRef.current === keyName)
) {
// eslint-disable-next-line sonarjs/no-identical-functions
setTimeout(() => {
fetchValueSuggestions({
key: keyName,
searchText,
fetchingComplete: true,
});
}, 300);
debouncedFetchValueSuggestions({
key: keyName,
searchText,
fetchingComplete: true,
});
}
// Process options to add appropriate formatting when selected
@ -1018,22 +1039,6 @@ function QuerySearch({
}
}
// // If no specific context is detected, provide general suggestions
// options = [
// ...(keySuggestions || []),
// { label: 'AND', type: 'conjunction', boost: -10 },
// { label: 'OR', type: 'conjunction', boost: -10 },
// { label: '(', type: 'parenthesis', info: 'Open group', boost: -20 },
// ];
// // Add space after selection for general context
// const optionsWithSpace = addSpaceToOptions(options);
// return {
// from: word?.from ?? 0,
// options: optionsWithSpace,
// };
// Don't show anything if no context detected
return {
from: word?.from ?? 0,
@ -1043,8 +1048,12 @@ function QuerySearch({
// Effect to handle focus state and trigger suggestions
useEffect(() => {
if (isFocused && editorRef.current) {
startCompletion(editorRef.current);
if (editorRef.current) {
if (!isFocused) {
closeCompletion(editorRef.current);
} else {
startCompletion(editorRef.current);
}
}
}, [isFocused]);
@ -1174,9 +1183,6 @@ function QuerySearch({
}}
onFocus={(): void => {
setIsFocused(true);
if (editorRef.current) {
startCompletion(editorRef.current);
}
}}
onBlur={handleBlur}
/>

View File

@ -20,6 +20,7 @@ import {
import { EQueryType } from 'types/common/dashboard';
import { DataSource } from 'types/common/queryBuilder';
import { extractQueryPairs } from 'utils/queryContextUtils';
import { unquote } from 'utils/stringUtils';
import { v4 as uuid } from 'uuid';
/**
@ -95,21 +96,6 @@ export const convertFiltersToExpression = (
};
};
function unquote(str: string): string {
if (typeof str !== 'string') return str;
const startsWithQuote = str.startsWith('"') || str.startsWith("'");
const endsWithSameQuote =
(str.endsWith('"') && str[0] === '"') ||
(str.endsWith("'") && str[0] === "'");
if (startsWithQuote && endsWithSameQuote && str.length >= 2) {
return str.slice(1, -1);
}
return str;
}
const formatValuesForFilter = (value: string | string[]): string | string[] => {
if (Array.isArray(value)) {
return value.map((v) => (typeof v === 'string' ? unquote(v) : String(v)));
@ -398,7 +384,7 @@ export const removeKeysFromExpression = (
currentQueryPair.position.operatorEnd ??
currentQueryPair.position.keyEnd;
// Get the part of the expression that comes after the current query pair
const expressionAfterPair = `${expression.slice(queryPairEnd + 1)}`;
const expressionAfterPair = `${updatedExpression.slice(queryPairEnd + 1)}`;
// Match optional spaces and an optional conjunction (AND/OR), case-insensitive
const conjunctionOrSpacesRegex = /^(\s*((AND|OR)\s+)?)/i;
const match = expressionAfterPair.match(conjunctionOrSpacesRegex);
@ -407,10 +393,10 @@ export const removeKeysFromExpression = (
queryPairEnd += match[0].length;
}
// Remove the full query pair (including any conjunction/whitespace) from the expression
updatedExpression = `${expression.slice(
updatedExpression = `${updatedExpression.slice(
0,
queryPairStart,
)}${expression.slice(queryPairEnd + 1)}`.trim();
)}${updatedExpression.slice(queryPairEnd + 1)}`.trim();
}
}
});

View File

@ -90,6 +90,10 @@ export default function QuickFilters(props: IQuickFiltersProps): JSX.Element {
...currentQuery.builder,
queryData: currentQuery.builder.queryData.map((item, idx) => ({
...item,
filter: {
...item.filter,
expression: '',
},
filters: {
...item.filters,
items: idx === lastUsedQuery ? [] : [...(item.filters?.items || [])],

View File

@ -865,6 +865,13 @@ export function QueryBuilderProvider({
...currentQueryData.builder,
queryData: currentQueryData.builder.queryData.map((item) => ({
...item,
filter: {
...item.filter,
expression:
item.filter?.expression.trim() === ''
? ''
: item.filter?.expression ?? '',
},
filters: {
items: [],
op: 'AND',

View File

@ -0,0 +1,13 @@
export function unquote(str: string): string {
if (typeof str !== 'string') return str;
const trimmed = str.trim();
const firstChar = trimmed[0];
const lastChar = trimmed[trimmed.length - 1];
if ((firstChar === '"' || firstChar === "'") && firstChar === lastChar) {
return trimmed.slice(1, -1);
}
return trimmed;
}