diff --git a/packages/grafana-ui/src/components/Select/types.ts b/packages/grafana-ui/src/components/Select/types.ts index e2ecb59178a..b7bd318463d 100644 --- a/packages/grafana-ui/src/components/Select/types.ts +++ b/packages/grafana-ui/src/components/Select/types.ts @@ -8,6 +8,7 @@ export type ActionMeta = SelectActionMeta<{}>; export type InputActionMeta = { action: 'set-value' | 'input-change' | 'input-blur' | 'menu-close'; }; +export type LoadOptionsCallback = (options: Array>) => void; export interface SelectCommonProps { /** Aria label applied to the input field */ @@ -87,8 +88,10 @@ export interface SelectCommonProps { export interface SelectAsyncProps { /** When specified as boolean the loadOptions will execute when component is mounted */ defaultOptions?: boolean | Array>; + /** Asynchronously load select options */ - loadOptions?: (query: string) => Promise>>; + loadOptions?: (query: string, cb?: LoadOptionsCallback) => Promise>> | void; + /** If cacheOptions is true, then the loaded data will be cached. The cache will remain until cacheOptions changes value. */ cacheOptions?: boolean; /** Message to display when options are loading */ diff --git a/public/app/core/components/Select/FolderPicker.tsx b/public/app/core/components/Select/FolderPicker.tsx index 878e7d9d9fb..b8d5c006372 100644 --- a/public/app/core/components/Select/FolderPicker.tsx +++ b/public/app/core/components/Select/FolderPicker.tsx @@ -3,7 +3,7 @@ import React, { PureComponent } from 'react'; import { AppEvents, SelectableValue } from '@grafana/data'; import { selectors } from '@grafana/e2e-selectors'; -import { ActionMeta, AsyncSelect } from '@grafana/ui'; +import { ActionMeta, AsyncSelect, LoadOptionsCallback } from '@grafana/ui'; import { contextSrv } from 'app/core/services/context_srv'; import { createFolder, getFolderById, searchFolders } from 'app/features/manage-dashboards/state/actions'; import { DashboardSearchHit } from 'app/features/search/types'; @@ -52,7 +52,7 @@ export class FolderPicker extends PureComponent { folder: null, }; - this.debouncedSearch = debounce(this.getOptions, 300, { + this.debouncedSearch = debounce(this.loadOptions, 300, { leading: true, trailing: true, }); @@ -82,7 +82,13 @@ export class FolderPicker extends PureComponent { await this.loadInitialValue(); }; - getOptions = async (query: string) => { + // when debouncing, we must use the callback form of react-select's loadOptions so we don't + // drop results for user input. This must not return a promise/use await. + loadOptions = (query: string, callback: LoadOptionsCallback): void => { + this.searchFolders(query).then(callback); + }; + + private searchFolders = async (query: string) => { const { rootName, enableReset, @@ -159,7 +165,7 @@ export class FolderPicker extends PureComponent { const resetFolder: SelectableValue = { label: initialTitle, value: undefined }; const rootFolder: SelectableValue = { label: rootName, value: 0 }; - const options = await this.getOptions(''); + const options = await this.searchFolders(''); let folder: SelectableValue | null = null; diff --git a/public/app/features/dashboard/components/DashboardSettings/DashboardSettings.test.tsx b/public/app/features/dashboard/components/DashboardSettings/DashboardSettings.test.tsx index a694be7c3f6..c0a60e5bb55 100644 --- a/public/app/features/dashboard/components/DashboardSettings/DashboardSettings.test.tsx +++ b/public/app/features/dashboard/components/DashboardSettings/DashboardSettings.test.tsx @@ -19,7 +19,7 @@ jest.mock('@grafana/runtime', () => ({ })); setBackendSrv({ - get: jest.fn().mockResolvedValue({}), + get: jest.fn().mockResolvedValue([]), } as any); describe('DashboardSettings', () => { diff --git a/public/app/features/dashboard/components/DashboardSettings/GeneralSettings.test.tsx b/public/app/features/dashboard/components/DashboardSettings/GeneralSettings.test.tsx index bbbaa0eda35..5d77b3db8bd 100644 --- a/public/app/features/dashboard/components/DashboardSettings/GeneralSettings.test.tsx +++ b/public/app/features/dashboard/components/DashboardSettings/GeneralSettings.test.tsx @@ -5,11 +5,16 @@ import { selectOptionInTest } from 'test/helpers/selectOptionInTest'; import { byRole } from 'testing-library-selector'; import { selectors } from '@grafana/e2e-selectors'; +import { setBackendSrv } from '@grafana/runtime'; import { DashboardModel } from '../../state'; import { GeneralSettingsUnconnected as GeneralSettings, Props } from './GeneralSettings'; +setBackendSrv({ + get: jest.fn().mockResolvedValue([]), +} as any); + const setupTestContext = (options: Partial) => { const defaults: Props = { dashboard: {