[User Experience App] Fixed searching filter values (#105582)
This commit is contained in:
parent
8ccf88e3e1
commit
84d4652b14
|
@ -15,6 +15,7 @@ import {
|
|||
EuiPopoverTitle,
|
||||
EuiSelectable,
|
||||
EuiSelectableOption,
|
||||
EuiLoadingSpinner,
|
||||
} from '@elastic/eui';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import styled from 'styled-components';
|
||||
|
@ -34,7 +35,13 @@ const formatOptions = (
|
|||
excludedValues?: string[],
|
||||
showCount?: boolean
|
||||
): EuiSelectableOption[] => {
|
||||
return (values ?? []).map(({ label, count }) => ({
|
||||
const uniqueValues: Record<string, number> = {};
|
||||
|
||||
values?.forEach(({ label, count }) => {
|
||||
uniqueValues[label] = count;
|
||||
});
|
||||
|
||||
return Object.entries(uniqueValues).map(([label, count]) => ({
|
||||
label,
|
||||
append: showCount ? (
|
||||
<Counter>
|
||||
|
@ -50,6 +57,7 @@ export function FieldValueSelection({
|
|||
fullWidth,
|
||||
label,
|
||||
loading,
|
||||
query,
|
||||
setQuery,
|
||||
button,
|
||||
width,
|
||||
|
@ -118,7 +126,7 @@ export function FieldValueSelection({
|
|||
hasActiveFilters={numOfFilters > 0}
|
||||
iconType="arrowDown"
|
||||
numActiveFilters={numOfFilters}
|
||||
numFilters={values.length}
|
||||
numFilters={options.length}
|
||||
onClick={onButtonClick}
|
||||
>
|
||||
{label}
|
||||
|
@ -158,19 +166,28 @@ export function FieldValueSelection({
|
|||
}),
|
||||
compressed,
|
||||
onInput: onValueChange,
|
||||
'data-test-subj': 'suggestionInputField',
|
||||
}}
|
||||
listProps={{
|
||||
onFocusBadge: false,
|
||||
}}
|
||||
options={options}
|
||||
onChange={onChange}
|
||||
isLoading={loading}
|
||||
isLoading={loading && !query && options.length === 0}
|
||||
allowExclusions={true}
|
||||
>
|
||||
{(list, search) => (
|
||||
<div style={{ width: 240 }}>
|
||||
<EuiPopoverTitle paddingSize="s">{search}</EuiPopoverTitle>
|
||||
{list}
|
||||
{loading && query && (
|
||||
<EuiText className="eui-textCenter" color="subdued">
|
||||
{i18n.translate('xpack.observability.fieldValueSelection.loading', {
|
||||
defaultMessage: 'Loading',
|
||||
})}{' '}
|
||||
<EuiLoadingSpinner size="m" />
|
||||
</EuiText>
|
||||
)}
|
||||
<EuiPopoverFooter paddingSize="s">
|
||||
<EuiButton
|
||||
fill
|
||||
|
|
|
@ -0,0 +1,131 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { FieldValueSuggestions } from './index';
|
||||
import { render, screen, fireEvent } from '@testing-library/react';
|
||||
import * as searchHook from '../../../hooks/use_es_search';
|
||||
import { EuiThemeProvider } from '../../../../../../../src/plugins/kibana_react/common';
|
||||
|
||||
jest.setTimeout(30000);
|
||||
|
||||
describe('FieldValueSuggestions', () => {
|
||||
jest.spyOn(HTMLElement.prototype, 'offsetHeight', 'get').mockReturnValue(1500);
|
||||
jest.spyOn(HTMLElement.prototype, 'offsetWidth', 'get').mockReturnValue(1500);
|
||||
|
||||
function setupSearch(data: any) {
|
||||
// @ts-ignore
|
||||
jest.spyOn(searchHook, 'useEsSearch').mockReturnValue({
|
||||
data: {
|
||||
took: 17,
|
||||
timed_out: false,
|
||||
_shards: { total: 35, successful: 35, skipped: 31, failed: 0 },
|
||||
hits: { total: { value: 15299, relation: 'eq' }, hits: [] },
|
||||
aggregations: {
|
||||
values: {
|
||||
doc_count_error_upper_bound: 0,
|
||||
sum_other_doc_count: 0,
|
||||
buckets: data,
|
||||
},
|
||||
},
|
||||
},
|
||||
loading: false,
|
||||
});
|
||||
}
|
||||
|
||||
it('renders a list', async () => {
|
||||
setupSearch([
|
||||
{ key: 'US', doc_count: 14132 },
|
||||
{ key: 'Pak', doc_count: 200 },
|
||||
{ key: 'Japan', doc_count: 100 },
|
||||
]);
|
||||
|
||||
render(
|
||||
<EuiThemeProvider darkMode={false}>
|
||||
<FieldValueSuggestions
|
||||
label="Service name"
|
||||
sourceField={'service'}
|
||||
onChange={() => {}}
|
||||
selectedValue={[]}
|
||||
filters={[]}
|
||||
asCombobox={false}
|
||||
/>
|
||||
</EuiThemeProvider>
|
||||
);
|
||||
|
||||
fireEvent.click(screen.getByText('Service name'));
|
||||
|
||||
expect(await screen.findByPlaceholderText('Filter Service name')).toBeInTheDocument();
|
||||
expect(await screen.findByText('Apply')).toBeInTheDocument();
|
||||
expect(await screen.findByText('US')).toBeInTheDocument();
|
||||
expect(await screen.findByText('Pak')).toBeInTheDocument();
|
||||
expect(await screen.findByText('Japan')).toBeInTheDocument();
|
||||
expect(await screen.findByText('14132')).toBeInTheDocument();
|
||||
expect(await screen.findByText('200')).toBeInTheDocument();
|
||||
expect(await screen.findByText('100')).toBeInTheDocument();
|
||||
|
||||
setupSearch([{ key: 'US', doc_count: 14132 }]);
|
||||
|
||||
fireEvent.input(screen.getByTestId('suggestionInputField'), {
|
||||
target: { value: 'u' },
|
||||
});
|
||||
|
||||
expect(await screen.findByDisplayValue('u')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('calls oncChange when applied', async () => {
|
||||
setupSearch([
|
||||
{ key: 'US', doc_count: 14132 },
|
||||
{ key: 'Pak', doc_count: 200 },
|
||||
{ key: 'Japan', doc_count: 100 },
|
||||
]);
|
||||
|
||||
const onChange = jest.fn();
|
||||
|
||||
const { rerender } = render(
|
||||
<EuiThemeProvider darkMode={false}>
|
||||
<FieldValueSuggestions
|
||||
label="Service name"
|
||||
sourceField={'service'}
|
||||
onChange={onChange}
|
||||
selectedValue={[]}
|
||||
filters={[]}
|
||||
asCombobox={false}
|
||||
/>
|
||||
</EuiThemeProvider>
|
||||
);
|
||||
|
||||
fireEvent.click(screen.getByText('Service name'));
|
||||
|
||||
fireEvent.click(await screen.findByText('US'));
|
||||
fireEvent.click(await screen.findByText('Apply'));
|
||||
|
||||
expect(onChange).toHaveBeenCalledTimes(1);
|
||||
expect(onChange).toHaveBeenCalledWith(['US'], []);
|
||||
|
||||
rerender(
|
||||
<EuiThemeProvider darkMode={false}>
|
||||
<FieldValueSuggestions
|
||||
label="Service name"
|
||||
sourceField={'service'}
|
||||
onChange={onChange}
|
||||
selectedValue={['US']}
|
||||
excludedValue={['Pak']}
|
||||
filters={[]}
|
||||
asCombobox={false}
|
||||
/>
|
||||
</EuiThemeProvider>
|
||||
);
|
||||
|
||||
fireEvent.click(await screen.findByText('US'));
|
||||
fireEvent.click(await screen.findByText('Pak'));
|
||||
fireEvent.click(await screen.findByText('Apply'));
|
||||
|
||||
expect(onChange).toHaveBeenCalledTimes(2);
|
||||
expect(onChange).toHaveBeenLastCalledWith([], ['US']);
|
||||
});
|
||||
});
|
|
@ -6,8 +6,6 @@
|
|||
*/
|
||||
|
||||
import React, { useState } from 'react';
|
||||
|
||||
import useDebounce from 'react-use/lib/useDebounce';
|
||||
import { useValuesList } from '../../../hooks/use_values_list';
|
||||
import { FieldValueSelection } from './field_value_selection';
|
||||
import { FieldValueSuggestionsProps } from './types';
|
||||
|
@ -35,7 +33,6 @@ export function FieldValueSuggestions({
|
|||
onChange: onSelectionChange,
|
||||
}: FieldValueSuggestionsProps) {
|
||||
const [query, setQuery] = useState('');
|
||||
const [debouncedValue, setDebouncedValue] = useState('');
|
||||
|
||||
const { values, loading } = useValuesList({
|
||||
indexPatternTitle,
|
||||
|
@ -46,14 +43,6 @@ export function FieldValueSuggestions({
|
|||
keepHistory: true,
|
||||
});
|
||||
|
||||
useDebounce(
|
||||
() => {
|
||||
setQuery(debouncedValue);
|
||||
},
|
||||
400,
|
||||
[debouncedValue]
|
||||
);
|
||||
|
||||
const SelectionComponent = asCombobox ? FieldValueCombobox : FieldValueSelection;
|
||||
|
||||
return (
|
||||
|
@ -63,7 +52,8 @@ export function FieldValueSuggestions({
|
|||
values={values}
|
||||
label={label}
|
||||
onChange={onSelectionChange}
|
||||
setQuery={setDebouncedValue}
|
||||
query={query}
|
||||
setQuery={setQuery}
|
||||
loading={loading}
|
||||
selectedValue={selectedValue}
|
||||
excludedValue={excludedValue}
|
||||
|
|
|
@ -39,6 +39,7 @@ export type FieldValueSelectionProps = CommonProps & {
|
|||
loading?: boolean;
|
||||
onChange: (val?: string[], excludedValue?: string[]) => void;
|
||||
values?: ListItem[];
|
||||
query?: string;
|
||||
setQuery: Dispatch<SetStateAction<string>>;
|
||||
};
|
||||
|
||||
|
|
|
@ -58,6 +58,13 @@ export const useValuesList = ({
|
|||
[query]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (!query) {
|
||||
// in case query is cleared, we don't wait for debounce
|
||||
setDebounceQuery(query);
|
||||
}
|
||||
}, [query]);
|
||||
|
||||
const { data, loading } = useEsSearch(
|
||||
createEsParams({
|
||||
index: indexPatternTitle!,
|
||||
|
|
Loading…
Reference in a new issue