Dashboards: Fix folder picker not showing correct results when typing too fast (#50303)

* Use callback form for loadOptions in folder picker

* clean up

* fix other tests

* clarify comment
This commit is contained in:
Josh Hunt 2022-06-10 15:54:21 +01:00 committed by GitHub
parent 307a0d4538
commit 32d4f6ac60
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 20 additions and 6 deletions

View File

@ -8,6 +8,7 @@ export type ActionMeta = SelectActionMeta<{}>;
export type InputActionMeta = {
action: 'set-value' | 'input-change' | 'input-blur' | 'menu-close';
};
export type LoadOptionsCallback<T> = (options: Array<SelectableValue<T>>) => void;
export interface SelectCommonProps<T> {
/** Aria label applied to the input field */
@ -87,8 +88,10 @@ export interface SelectCommonProps<T> {
export interface SelectAsyncProps<T> {
/** When specified as boolean the loadOptions will execute when component is mounted */
defaultOptions?: boolean | Array<SelectableValue<T>>;
/** Asynchronously load select options */
loadOptions?: (query: string) => Promise<Array<SelectableValue<T>>>;
loadOptions?: (query: string, cb?: LoadOptionsCallback<T>) => Promise<Array<SelectableValue<T>>> | 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 */

View File

@ -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<Props, State> {
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<Props, State> {
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<number>): void => {
this.searchFolders(query).then(callback);
};
private searchFolders = async (query: string) => {
const {
rootName,
enableReset,
@ -159,7 +165,7 @@ export class FolderPicker extends PureComponent<Props, State> {
const resetFolder: SelectableValue<number> = { label: initialTitle, value: undefined };
const rootFolder: SelectableValue<number> = { label: rootName, value: 0 };
const options = await this.getOptions('');
const options = await this.searchFolders('');
let folder: SelectableValue<number> | null = null;

View File

@ -19,7 +19,7 @@ jest.mock('@grafana/runtime', () => ({
}));
setBackendSrv({
get: jest.fn().mockResolvedValue({}),
get: jest.fn().mockResolvedValue([]),
} as any);
describe('DashboardSettings', () => {

View File

@ -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<Props>) => {
const defaults: Props = {
dashboard: {