Skip to content

Commit cd1d6fe

Browse files
TheSpacyCatBenediktSeidl
authored andcommitted
CheckMKAsyncSelect: resolve "element not found"
Before this change it was possible that a CheckMkAsyncSelect could not find the saved value in the elements loaded by default and would display an error message, although the query was working correctly.
1 parent 0d3305d commit cd1d6fe

File tree

2 files changed

+57
-24
lines changed

2 files changed

+57
-24
lines changed

src/ui/components.tsx

Lines changed: 34 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import {
1212
Label,
1313
VerticalGroup,
1414
} from '@grafana/ui';
15-
import { debounce } from 'lodash';
15+
import { debounce, isNull } from 'lodash';
1616
import React, { JSXElementConstructor } from 'react';
1717

1818
import {
@@ -38,6 +38,14 @@ interface CheckMkAsyncSelectProps<Key extends keyof RequestSpec, Value = Request
3838
inputId: string; // make the InlineField magic do its work // TODO: find better solution
3939
}
4040

41+
function findValueInOptions<Value>(lookupOptions: Array<NonNullable<SelectableValue<Value>>>, value: Value) {
42+
const result = lookupOptions.find((opt) => opt.value === value);
43+
if (result !== undefined) {
44+
return result;
45+
}
46+
return null;
47+
}
48+
4149
export const CheckMkAsyncSelect = function <Key extends keyof RequestSpec, Value = RequestSpec[Key]>(
4250
props: CheckMkAsyncSelectProps<Key, Value>
4351
) {
@@ -46,14 +54,6 @@ export const CheckMkAsyncSelect = function <Key extends keyof RequestSpec, Value
4654
const [counter, setCounter] = React.useState(0);
4755
const [autocompleteError, setAutocompleteError] = React.useState('');
4856

49-
function findValueInOptions() {
50-
const result = options.find((opt) => opt.value === value);
51-
if (result) {
52-
return result;
53-
}
54-
return null;
55-
}
56-
5757
function getPlaceholder() {
5858
if (autocompleteError !== '') {
5959
return autocompleteError;
@@ -68,22 +68,32 @@ export const CheckMkAsyncSelect = function <Key extends keyof RequestSpec, Value
6868
}
6969

7070
const loadOptions = React.useCallback(
71-
(inputValue: string): Promise<Array<SelectableValue<Value>>> => {
72-
return autocompleter(inputValue).then(
73-
(data) => {
74-
setAutocompleteError('');
75-
setOptions(data);
76-
return data;
77-
},
78-
(error) => {
79-
if (error && error.message) {
80-
setAutocompleteError(error.message);
81-
}
82-
throw error;
71+
async (inputValue: string): Promise<Array<SelectableValue<Value>>> => {
72+
try {
73+
const data = await autocompleter(inputValue);
74+
setAutocompleteError('');
75+
if (value !== null && value !== undefined && inputValue === '' && isNull(findValueInOptions(data, value))) {
76+
// when we load the query editor with a saved configuration it is
77+
// possible, that the value saved is not present in the autocompleter
78+
// values which got queried with an empty string. (the autocompleter
79+
// only returns the first 100 matching elements).
80+
// this would mean we would display an error "could not find element 'xxx'"
81+
// in order to prevent that, we do an additional query, with the
82+
// value to make sure we receive the value (and label).
83+
const specificData = await autocompleter(value as string);
84+
data.push(...specificData);
85+
}
86+
setOptions(data);
87+
return data;
88+
} catch (e) {
89+
const error = e as Error;
90+
if (error && error.message) {
91+
setAutocompleteError(error.message);
8392
}
84-
);
93+
throw error;
94+
}
8595
},
86-
[autocompleter]
96+
[autocompleter, value]
8797
);
8898

8999
React.useEffect(() => {
@@ -108,7 +118,7 @@ export const CheckMkAsyncSelect = function <Key extends keyof RequestSpec, Value
108118
key={`${Math.max(1, counter)}`} // ignore the first update
109119
loadOptions={loadOptions}
110120
width={width || 32}
111-
value={findValueInOptions()}
121+
value={findValueInOptions(options, value)}
112122
placeholder={getPlaceholder()}
113123
/>
114124
);
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import { SelectableValue } from '@grafana/data';
2+
import { render } from '@testing-library/react';
3+
import * as React from 'react';
4+
5+
import { CheckMkAsyncSelect } from '../../../src/ui/components';
6+
7+
describe('CheckMkAsyncSelect', () => {
8+
const autocompleter = jest.fn(async (prefix: string): Promise<Array<SelectableValue<string>>> => {
9+
if (prefix === 'sentinel') {
10+
return [{ label: 'sentinel', value: 'sentinel' }];
11+
}
12+
return [];
13+
});
14+
15+
it("uses the specific options if the default ones don't include the value", async () => {
16+
const screen = await render(
17+
<CheckMkAsyncSelect autocompleter={autocompleter} inputId={'foo'} onChange={() => undefined} value="sentinel" />
18+
);
19+
20+
expect(autocompleter).toHaveBeenCalledWith('');
21+
expect(autocompleter).toHaveBeenCalledWith('sentinel');
22+
});
23+
});

0 commit comments

Comments
 (0)