Skip to content

Commit

Permalink
Merge branch 'feat/4312-search-bar-replace-tablewzapi' of https://git…
Browse files Browse the repository at this point in the history
…hub.com/wazuh/wazuh-kibana-app into feat/4312-search-bar-replace-management-groups
  • Loading branch information
Desvelao committed Aug 10, 2023
2 parents bb9bfcd + a2abff5 commit 891f86c
Show file tree
Hide file tree
Showing 4 changed files with 96 additions and 56 deletions.
3 changes: 3 additions & 0 deletions plugins/main/common/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2256,3 +2256,6 @@ export const MODULE_SCA_CHECK_RESULT_LABEL = {
export const SEARCH_BAR_WQL_VALUE_SUGGESTIONS_COUNT = 30;
// This limits the suggestions for the token of type value displayed in the search bar
export const SEARCH_BAR_WQL_VALUE_SUGGESTIONS_DISPLAY_COUNT = 10;
/* Time in milliseconds to debounce the analysis of search bar. This mitigates some problems related
to changes running in parallel */
export const SEARCH_BAR_DEBOUNCE_UPDATE_TIME = 400;
134 changes: 81 additions & 53 deletions plugins/main/public/components/search-bar/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,21 +9,22 @@ import {
EuiSelect,
EuiText,
EuiFlexGroup,
EuiFlexItem
EuiFlexItem,
} from '@elastic/eui';
import { EuiSuggest } from '../eui-suggest';
import { searchBarQueryLanguages } from './query-language';
import _ from 'lodash';
import { ISearchBarModeWQL } from './query-language/wql';
import { SEARCH_BAR_DEBOUNCE_UPDATE_TIME } from '../../../common/constants';

export interface SearchBarProps{
export interface SearchBarProps {
defaultMode?: string;
modes: ISearchBarModeWQL[];
onChange?: (params: any) => void;
onSearch: (params: any) => void;
buttonsRender?: () => React.ReactNode
buttonsRender?: () => React.ReactNode;
input?: string;
};
}

export const SearchBar = ({
defaultMode,
Expand Down Expand Up @@ -54,12 +55,16 @@ export const SearchBar = ({
output: undefined,
});
// Cache the previous output
const queryLanguageOutputRunPreviousOutput = useRef(queryLanguageOutputRun.output);
const queryLanguageOutputRunPreviousOutput = useRef(
queryLanguageOutputRun.output,
);
// Controls when the suggestion popover is open/close
const [isOpenSuggestionPopover, setIsOpenSuggestionPopover] =
useState<boolean>(false);
// Reference to the input
const inputRef = useRef();
// Debounce update timer
const debounceUpdateSearchBarTimer = useRef();

// Handler when searching
const _onSearch = (output: any) => {
Expand All @@ -79,55 +84,69 @@ export const SearchBar = ({
}
};

const selectedQueryLanguageParameters = modes.find(({ id }) => id === queryLanguage.id);
const selectedQueryLanguageParameters = modes.find(
({ id }) => id === queryLanguage.id,
);

useEffect(() => {
// React to external changes and set the internal input text. Use the `transformInput` of
// the query language in use
rest.input && searchBarQueryLanguages[queryLanguage.id]?.transformInput && setInput(
searchBarQueryLanguages[queryLanguage.id]?.transformInput?.(
rest.input,
{
configuration: queryLanguage.configuration,
parameters: selectedQueryLanguageParameters,
}
),
);
rest.input &&
searchBarQueryLanguages[queryLanguage.id]?.transformInput &&
setInput(
searchBarQueryLanguages[queryLanguage.id]?.transformInput?.(
rest.input,
{
configuration: queryLanguage.configuration,
parameters: selectedQueryLanguageParameters,
},
),
);
}, [rest.input]);

useEffect(() => {
(async () => {
// Set the query language output
const queryLanguageOutput = await searchBarQueryLanguages[queryLanguage.id].run(input, {
onSearch: _onSearch,
setInput,
closeSuggestionPopover: () => setIsOpenSuggestionPopover(false),
openSuggestionPopover: () => setIsOpenSuggestionPopover(true),
setQueryLanguageConfiguration: (configuration: any) =>
setQueryLanguage(state => ({
...state,
configuration:
configuration?.(state.configuration) || configuration,
})),
setQueryLanguageOutput: setQueryLanguageOutputRun,
inputRef,
queryLanguage: {
configuration: queryLanguage.configuration,
parameters: selectedQueryLanguageParameters,
},
});
queryLanguageOutputRunPreviousOutput.current = {
...queryLanguageOutputRun.output
};
setQueryLanguageOutputRun(queryLanguageOutput);
debounceUpdateSearchBarTimer.current &&
clearTimeout(debounceUpdateSearchBarTimer.current);
// Debounce the updating of the search bar state
debounceUpdateSearchBarTimer.current = setTimeout(async () => {
const queryLanguageOutput = await searchBarQueryLanguages[
queryLanguage.id
].run(input, {
onSearch: _onSearch,
setInput,
closeSuggestionPopover: () => setIsOpenSuggestionPopover(false),
openSuggestionPopover: () => setIsOpenSuggestionPopover(true),
setQueryLanguageConfiguration: (configuration: any) =>
setQueryLanguage(state => ({
...state,
configuration:
configuration?.(state.configuration) || configuration,
})),
setQueryLanguageOutput: setQueryLanguageOutputRun,
inputRef,
queryLanguage: {
configuration: queryLanguage.configuration,
parameters: selectedQueryLanguageParameters,
},
});
queryLanguageOutputRunPreviousOutput.current = {
...queryLanguageOutputRun.output,
};
setQueryLanguageOutputRun(queryLanguageOutput);
}, SEARCH_BAR_DEBOUNCE_UPDATE_TIME);
})();
}, [input, queryLanguage, selectedQueryLanguageParameters?.options]);

useEffect(() => {
onChange
// Ensure the previous output is different to the new one
&& !_.isEqual(queryLanguageOutputRun.output, queryLanguageOutputRunPreviousOutput.current)
&& onChange(queryLanguageOutputRun.output);
onChange &&
// Ensure the previous output is different to the new one
!_.isEqual(
queryLanguageOutputRun.output,
queryLanguageOutputRunPreviousOutput.current,
) &&
onChange(queryLanguageOutputRun.output);
}, [queryLanguageOutputRun.output]);

const onQueryLanguagePopoverSwitch = () =>
Expand Down Expand Up @@ -163,7 +182,7 @@ export const SearchBar = ({
closePopover={onQueryLanguagePopoverSwitch}
>
<EuiPopoverTitle>SYNTAX OPTIONS</EuiPopoverTitle>
<div style={{width: '350px'}}>
<div style={{ width: '350px' }}>
<EuiText>
{searchBarQueryLanguages[queryLanguage.id].description}
</EuiText>
Expand All @@ -173,7 +192,8 @@ export const SearchBar = ({
<div>
<EuiLink
href={
searchBarQueryLanguages[queryLanguage.id].documentationLink
searchBarQueryLanguages[queryLanguage.id]
.documentationLink
}
target='__blank'
rel='noopener noreferrer'
Expand All @@ -194,7 +214,9 @@ export const SearchBar = ({
text: searchBarQueryLanguages[id].label,
}))}
value={queryLanguage.id}
onChange={(event: React.ChangeEvent<HTMLSelectElement>) => {
onChange={(
event: React.ChangeEvent<HTMLSelectElement>,
) => {
const queryLanguageID: string = event.target.value;
setQueryLanguage({
id: queryLanguageID,
Expand All @@ -217,13 +239,19 @@ export const SearchBar = ({
/>
</>
);
return rest.buttonsRender || queryLanguageOutputRun.filterButtons
? (
<EuiFlexGroup>
<EuiFlexItem>{searchBar}</EuiFlexItem>
{rest.buttonsRender && <EuiFlexItem grow={false}>{rest.buttonsRender()}</EuiFlexItem>}
{queryLanguageOutputRun.filterButtons && <EuiFlexItem grow={false}>{queryLanguageOutputRun.filterButtons}</EuiFlexItem>}
</EuiFlexGroup>
)
: searchBar;
return rest.buttonsRender || queryLanguageOutputRun.filterButtons ? (
<EuiFlexGroup>
<EuiFlexItem>{searchBar}</EuiFlexItem>
{rest.buttonsRender && (
<EuiFlexItem grow={false}>{rest.buttonsRender()}</EuiFlexItem>
)}
{queryLanguageOutputRun.filterButtons && (
<EuiFlexItem grow={false}>
{queryLanguageOutputRun.filterButtons}
</EuiFlexItem>
)}
</EuiFlexGroup>
) : (
searchBar
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -303,7 +303,7 @@ describe('Query language - WQL', () => {
${'(field=value or field2>'} | ${{ type: { iconType: 'kqlOperand', color: 'tint1' }, label: '~' }} | ${'(field=value or field2~'}
${'(field=value or field2>'} | ${{ type: { iconType: 'kqlValue', color: 'tint0' }, label: 'value2' }} | ${'(field=value or field2>value2'}
${'(field=value or field2>value2'} | ${{ type: { iconType: 'kqlValue', color: 'tint0' }, label: 'value3' }} | ${'(field=value or field2>value3'}
${'(field=value or field2>value2'} | ${{ type: { iconType: 'tokenDenseVector', color: 'tint3' }, label: ')' }} | ${'(field=value or field2>value2)'}
${'(field=value or field2>value2'} | ${{ type: { iconType: 'tokenDenseVector', color: 'tint3' }, label: ')' }} | ${'(field=value or field2>value2 )'}
`(
'click suggestion - WQL $WQL => $changedInput',
async ({ WQL: currentInput, clikedSuggestion, changedInput }) => {
Expand Down
13 changes: 11 additions & 2 deletions plugins/main/public/components/search-bar/query-language/wql.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -102,10 +102,14 @@ const suggestionMappingLanguageTokenType = {
* @returns
*/
function mapSuggestionCreator(type: ITokenType) {
return function ({ ...params }) {
return function ({ label, ...params }) {
return {
type,
...params,
/* WORKAROUND: ensure the label is a string. If it is not a string, an warning is
displayed in the console related to prop types
*/
...(typeof label !== 'undefined' ? { label: String(label) } : {}),
};
};
}
Expand Down Expand Up @@ -1094,10 +1098,15 @@ export const WQL = {
: item.label;
} else {
// add a whitespace for conjunction <whitespace><conjunction>
// add a whitespace for grouping operator <whitespace>)
!/\s$/.test(input) &&
(item.type.iconType ===
suggestionMappingLanguageTokenType.conjunction.iconType ||
lastToken?.type === 'conjunction') &&
lastToken?.type === 'conjunction' ||
(item.type.iconType ===
suggestionMappingLanguageTokenType.operator_group
.iconType &&
item.label === ')')) &&
tokens.push({
type: 'whitespace',
value: ' ',
Expand Down

0 comments on commit 891f86c

Please sign in to comment.