Jaeger: Update operations dropdown (#49329)

* Fix issue where no values would load in operations dropdown

* Better naming

* Updated tests
This commit is contained in:
Joey Tawadrous
2022-05-22 10:29:25 +01:00
committed by GitHub
parent 4f46c2f75f
commit f06d9164a6
2 changed files with 16 additions and 36 deletions
@@ -113,12 +113,10 @@ describe('SearchForm', () => {
expect(asyncServiceSelect).toBeInTheDocument(); expect(asyncServiceSelect).toBeInTheDocument();
await user.type(asyncServiceSelect, 'j'); await user.type(asyncServiceSelect, 'j');
jest.advanceTimersByTime(3000);
var option = await screen.findByText('jaeger-query'); var option = await screen.findByText('jaeger-query');
expect(option).toBeDefined(); expect(option).toBeDefined();
await user.type(asyncServiceSelect, 'c'); await user.type(asyncServiceSelect, 'c');
jest.advanceTimersByTime(3000);
option = await screen.findByText('No options found'); option = await screen.findByText('No options found');
expect(option).toBeDefined(); expect(option).toBeDefined();
}); });
@@ -1,9 +1,8 @@
import { css } from '@emotion/css'; import { css } from '@emotion/css';
import { debounce } from 'lodash';
import React, { useCallback, useEffect, useState } from 'react'; import React, { useCallback, useEffect, useState } from 'react';
import { SelectableValue } from '@grafana/data'; import { SelectableValue } from '@grafana/data';
import { AsyncSelect, fuzzyMatch, InlineField, InlineFieldRow, Input } from '@grafana/ui'; import { fuzzyMatch, InlineField, InlineFieldRow, Input, Select } from '@grafana/ui';
import { notifyApp } from 'app/core/actions'; import { notifyApp } from 'app/core/actions';
import { createErrorNotification } from 'app/core/copy/appNotification'; import { createErrorNotification } from 'app/core/copy/appNotification';
import { dispatch } from 'app/store/store'; import { dispatch } from 'app/store/store';
@@ -37,7 +36,7 @@ export function SearchForm({ datasource, query, onChange }: Props) {
operations: false, operations: false,
}); });
const loadServices = useCallback( const loadOptions = useCallback(
async (url: string, loaderOfType: string, query = ''): Promise<Array<SelectableValue<string>>> => { async (url: string, loaderOfType: string, query = ''): Promise<Array<SelectableValue<string>>> => {
setIsLoading((prevValue) => ({ ...prevValue, [loaderOfType]: true })); setIsLoading((prevValue) => ({ ...prevValue, [loaderOfType]: true }));
@@ -47,14 +46,12 @@ export function SearchForm({ datasource, query, onChange }: Props) {
return [{ label: `No ${loaderOfType} found`, value: `No ${loaderOfType} found` }]; return [{ label: `No ${loaderOfType} found`, value: `No ${loaderOfType} found` }];
} }
const serviceOptions: SelectableValue[] = values.sort().map((service) => ({ const options: SelectableValue[] = values.sort().map((option) => ({
label: service, label: option,
value: service, value: option,
})); }));
const filteredOptions = serviceOptions.filter((item) => const filteredOptions = options.filter((item) => (item.value ? fuzzyMatch(item.value, query).found : false));
item.value ? fuzzyMatch(item.value, query).found : false
);
return filteredOptions; return filteredOptions;
} catch (error) { } catch (error) {
dispatch(notifyApp(createErrorNotification('Error', error))); dispatch(notifyApp(createErrorNotification('Error', error)));
@@ -66,28 +63,17 @@ export function SearchForm({ datasource, query, onChange }: Props) {
[datasource] [datasource]
); );
const getServiceOptions = (userQuery: string) => {
return loadServices('/api/services', 'services', userQuery);
};
const getOperationOptions = (userQuery: string) => {
return loadServices(`/api/services/${encodeURIComponent(query.service!)}/operations`, 'operations', userQuery);
};
const serviceSearch = debounce(getServiceOptions, 500, { leading: true, trailing: true });
const operationSearch = debounce(getOperationOptions, 500, { leading: true, trailing: true });
useEffect(() => { useEffect(() => {
const getServices = async () => { const getServices = async () => {
const services = await loadServices('/api/services', 'services'); const services = await loadOptions('/api/services', 'services');
setServiceOptions(services); setServiceOptions(services);
}; };
getServices(); getServices();
}, [datasource, loadServices]); }, [datasource, loadOptions]);
useEffect(() => { useEffect(() => {
const getOperations = async () => { const getOperations = async () => {
const operations = await loadServices( const operations = await loadOptions(
`/api/services/${encodeURIComponent(query.service!)}/operations`, `/api/services/${encodeURIComponent(query.service!)}/operations`,
'operations' 'operations'
); );
@@ -96,17 +82,16 @@ export function SearchForm({ datasource, query, onChange }: Props) {
if (query.service) { if (query.service) {
getOperations(); getOperations();
} }
}, [datasource, query.service, loadServices]); }, [datasource, query.service, loadOptions]);
return ( return (
<div className={css({ maxWidth: '500px' })}> <div className={css({ maxWidth: '500px' })}>
<InlineFieldRow> <InlineFieldRow>
<InlineField label="Service" labelWidth={14} grow> <InlineField label="Service" labelWidth={14} grow>
<AsyncSelect <Select
inputId="service" inputId="service"
cacheOptions={false} options={serviceOptions}
loadOptions={serviceSearch} onOpenMenu={() => loadOptions('/api/services', 'services')}
onOpenMenu={() => loadServices('/api/services', 'services')}
isLoading={isLoading.services} isLoading={isLoading.services}
value={serviceOptions?.find((v) => v?.value === query.service) || undefined} value={serviceOptions?.find((v) => v?.value === query.service) || undefined}
onChange={(v) => onChange={(v) =>
@@ -118,19 +103,17 @@ export function SearchForm({ datasource, query, onChange }: Props) {
} }
menuPlacement="bottom" menuPlacement="bottom"
isClearable isClearable
defaultOptions
aria-label={'select-service-name'} aria-label={'select-service-name'}
/> />
</InlineField> </InlineField>
</InlineFieldRow> </InlineFieldRow>
<InlineFieldRow> <InlineFieldRow>
<InlineField label="Operation" labelWidth={14} grow disabled={!query.service}> <InlineField label="Operation" labelWidth={14} grow disabled={!query.service}>
<AsyncSelect <Select
inputId="operation" inputId="operation"
cacheOptions={false} options={operationOptions}
loadOptions={operationSearch}
onOpenMenu={() => onOpenMenu={() =>
loadServices(`/api/services/${encodeURIComponent(query.service!)}/operations`, 'operations') loadOptions(`/api/services/${encodeURIComponent(query.service!)}/operations`, 'operations')
} }
isLoading={isLoading.operations} isLoading={isLoading.operations}
value={operationOptions?.find((v) => v.value === query.operation) || null} value={operationOptions?.find((v) => v.value === query.operation) || null}
@@ -142,7 +125,6 @@ export function SearchForm({ datasource, query, onChange }: Props) {
} }
menuPlacement="bottom" menuPlacement="bottom"
isClearable isClearable
defaultOptions
aria-label={'select-operation-name'} aria-label={'select-operation-name'}
/> />
</InlineField> </InlineField>