diff --git a/public/app/plugins/datasource/tempo/QueryEditor/NativeSearch.test.tsx b/public/app/plugins/datasource/tempo/QueryEditor/NativeSearch.test.tsx index 21ae2466c0c..dc01b7262f4 100644 --- a/public/app/plugins/datasource/tempo/QueryEditor/NativeSearch.test.tsx +++ b/public/app/plugins/datasource/tempo/QueryEditor/NativeSearch.test.tsx @@ -35,6 +35,7 @@ const mockQuery = { queryType: 'nativeSearch', key: 'Q-595a9bbc-2a25-49a7-9249-a52a0a475d83-0', serviceName: 'driver', + spanName: 'customer', } as TempoQuery; describe('NativeSearch', () => { @@ -56,9 +57,9 @@ describe('NativeSearch', () => { ); - const asyncServiceSelect = screen.getByRole('combobox', { name: 'select-span-name' }); + const select = screen.getByRole('combobox', { name: 'select-service-name' }); - await user.click(asyncServiceSelect); + await user.click(select); const loader = screen.getByText('Loading options...'); expect(loader).toBeInTheDocument(); @@ -76,7 +77,7 @@ describe('NativeSearch', () => { queryType: 'nativeSearch', refId: 'A', serviceName: 'driver', - spanName: 'driver', + spanName: 'customer', }; render( @@ -88,12 +89,13 @@ describe('NativeSearch', () => { /> ); - const asyncServiceSelect = await screen.findByRole('combobox', { name: 'select-span-name' }); + const select = await screen.findByRole('combobox', { name: 'select-service-name' }); - expect(asyncServiceSelect).toBeInTheDocument(); - await user.click(asyncServiceSelect); + expect(select).toBeInTheDocument(); + await user.click(select); jest.advanceTimersByTime(1000); + await user.type(select, 'd'); const driverOption = await screen.findByText('driver'); await user.click(driverOption); @@ -105,19 +107,16 @@ describe('NativeSearch', () => { {}} onRunQuery={() => {}} /> ); - const asyncServiceSelect = await screen.findByRole('combobox', { name: 'select-span-name' }); - - expect(asyncServiceSelect).toBeInTheDocument(); - await user.click(asyncServiceSelect); + const select = await screen.findByRole('combobox', { name: 'select-service-name' }); + await user.click(select); jest.advanceTimersByTime(1000); + expect(select).toBeInTheDocument(); - await user.type(asyncServiceSelect, 'd'); - jest.advanceTimersByTime(1000); + await user.type(select, 'd'); var option = await screen.findByText('driver'); expect(option).toBeDefined(); - await user.type(asyncServiceSelect, 'a'); - jest.advanceTimersByTime(1000); + await user.type(select, 'a'); option = await screen.findByText('No options found'); expect(option).toBeDefined(); }); diff --git a/public/app/plugins/datasource/tempo/QueryEditor/NativeSearch.tsx b/public/app/plugins/datasource/tempo/QueryEditor/NativeSearch.tsx index 232c86aed93..4e83d2808b5 100644 --- a/public/app/plugins/datasource/tempo/QueryEditor/NativeSearch.tsx +++ b/public/app/plugins/datasource/tempo/QueryEditor/NativeSearch.tsx @@ -1,5 +1,4 @@ import { css } from '@emotion/css'; -import { debounce } from 'lodash'; import Prism from 'prismjs'; import React, { useCallback, useState, useEffect, useMemo } from 'react'; import { Node } from 'slate'; @@ -15,10 +14,10 @@ import { BracesPlugin, TypeaheadInput, TypeaheadOutput, - AsyncSelect, Alert, useStyles2, fuzzyMatch, + Select, } from '@grafana/ui'; import { notifyApp } from 'app/core/actions'; import { createErrorNotification } from 'app/core/copy/appNotification'; @@ -52,12 +51,8 @@ const NativeSearch = ({ datasource, query, onChange, onBlur, onRunQuery }: Props const styles = useStyles2(getStyles); const languageProvider = useMemo(() => new TempoLanguageProvider(datasource), [datasource]); const [hasSyntaxLoaded, setHasSyntaxLoaded] = useState(false); - const [asyncServiceNameValue, setAsyncServiceNameValue] = useState>({ - value: '', - }); - const [asyncSpanNameValue, setAsyncSpanNameValue] = useState>({ - value: '', - }); + const [serviceOptions, setServiceOptions] = useState>>(); + const [spanOptions, setSpanOptions] = useState>>(); const [error, setError] = useState(null); const [inputErrors, setInputErrors] = useState<{ [key: string]: boolean }>({}); const [isLoading, setIsLoading] = useState<{ @@ -68,60 +63,35 @@ const NativeSearch = ({ datasource, query, onChange, onBlur, onRunQuery }: Props spanName: false, }); - async function fetchOptionsCallback(name: string, lp: TempoLanguageProvider, query = '') { - try { - setIsLoading((prevValue) => ({ ...prevValue, [name]: false })); - const options = await lp.getOptions(name); - const filteredOptions = options.filter((item) => (item.value ? fuzzyMatch(item.value, query).found : false)); - return filteredOptions; - } catch (error) { - if (error?.status === 404) { - setIsLoading((prevValue) => ({ ...prevValue, [name]: false })); - } else { - dispatch(notifyApp(createErrorNotification('Error', error))); + const loadOptions = useCallback( + async (name: string, query = '') => { + const lpName = name === 'serviceName' ? 'service.name' : 'name'; + setIsLoading((prevValue) => ({ ...prevValue, [name]: true })); + + try { + const options = await languageProvider.getOptions(lpName); + const filteredOptions = options.filter((item) => (item.value ? fuzzyMatch(item.value, query).found : false)); + return filteredOptions; + } catch (error) { + if (error?.status === 404) { + setError(error); + } else { + dispatch(notifyApp(createErrorNotification('Error', error))); + } + return []; + } finally { setIsLoading((prevValue) => ({ ...prevValue, [name]: false })); } - setError(error); - return []; - } - } - - const loadOptionsOfType = useCallback( - (name: string) => { - setIsLoading((prevValue) => ({ ...prevValue, [name]: true })); - return fetchOptionsCallback(name, languageProvider); }, [languageProvider] ); - const fetchOptionsOfType = useCallback( - (name: string) => debounce(() => loadOptionsOfType(name), 500, { leading: true, trailing: true }), - [loadOptionsOfType] - ); - - const getNameOptions = (query: string, name: string) => { - setIsLoading((prevValue) => ({ ...prevValue, [name]: true })); - return fetchOptionsCallback(name, languageProvider, query); - }; - - const getServiceNameOptions = (query: string) => { - return getNameOptions(query, 'service.name'); - }; - - const getSpanNameOptions = (query: string) => { - return getNameOptions(query, 'name'); - }; - - const serviceNameSearch = debounce(getServiceNameOptions, 500, { leading: true, trailing: true }); - const spanNameSearch = debounce(getSpanNameOptions, 500, { leading: true, trailing: true }); - useEffect(() => { const fetchOptions = async () => { try { - await languageProvider.start(); - fetchOptionsCallback('service.name', languageProvider); - fetchOptionsCallback('name', languageProvider); - setHasSyntaxLoaded(true); + const [services, spans] = await Promise.all([loadOptions('serviceName'), loadOptions('spanName')]); + setServiceOptions(services); + setSpanOptions(spans); } catch (error) { // Display message if Tempo is connected but search 404's if (error?.status === 404) { @@ -129,11 +99,22 @@ const NativeSearch = ({ datasource, query, onChange, onBlur, onRunQuery }: Props } else { dispatch(notifyApp(createErrorNotification('Error', error))); } - setHasSyntaxLoaded(true); } }; fetchOptions(); - }, [languageProvider, fetchOptionsOfType]); + }, [languageProvider, loadOptions]); + + useEffect(() => { + const fetchTags = async () => { + try { + await languageProvider.start(); + setHasSyntaxLoaded(true); + } catch (error) { + dispatch(notifyApp(createErrorNotification('Error', error))); + } + }; + fetchTags(); + }, [languageProvider]); const onTypeahead = async (typeahead: TypeaheadInput): Promise => { return await languageProvider.provideCompletionItems(typeahead); @@ -160,17 +141,15 @@ const NativeSearch = ({ datasource, query, onChange, onBlur, onRunQuery }: Props
- { + loadOptions('serviceName'); + }} isLoading={isLoading.serviceName} - value={asyncServiceNameValue.value} + value={serviceOptions?.find((v) => v?.value === query.serviceName) || undefined} onChange={(v) => { - setAsyncServiceNameValue({ - value: v, - }); onChange({ ...query, serviceName: v?.value || undefined, @@ -178,7 +157,6 @@ const NativeSearch = ({ datasource, query, onChange, onBlur, onRunQuery }: Props }} placeholder="Select a service" isClearable - defaultOptions onKeyDown={onKeyDown} aria-label={'select-service-name'} /> @@ -186,15 +164,15 @@ const NativeSearch = ({ datasource, query, onChange, onBlur, onRunQuery }: Props - { + loadOptions('spanName'); + }} isLoading={isLoading.spanName} - value={asyncSpanNameValue.value} + value={spanOptions?.find((v) => v?.value === query.spanName) || undefined} onChange={(v) => { - setAsyncSpanNameValue({ value: v }); onChange({ ...query, spanName: v?.value || undefined, @@ -202,7 +180,6 @@ const NativeSearch = ({ datasource, query, onChange, onBlur, onRunQuery }: Props }} placeholder="Select a span" isClearable - defaultOptions onKeyDown={onKeyDown} aria-label={'select-span-name'} />