Alerting: allows clearing the datasource selection in rule list (#41264)

This commit is contained in:
Gilles De Mey
2021-11-09 18:20:36 +01:00
committed by GitHub
parent f45eb309ef
commit 2bc30daa49
14 changed files with 109 additions and 20 deletions

View File

@@ -39,6 +39,8 @@
"@grafana/tsconfig": "^1.0.0-rc1",
"@rollup/plugin-commonjs": "21.0.1",
"@rollup/plugin-node-resolve": "13.0.6",
"@testing-library/react": "^12.1.2",
"@testing-library/user-event": "^13.5.0",
"@types/angular": "1.6.56",
"@types/history": "^4.7.8",
"@types/jest": "27.0.2",

View File

@@ -0,0 +1,27 @@
import React from 'react';
import { DataSourcePicker } from './DataSourcePicker';
import { render } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
jest.mock('../services/dataSourceSrv');
describe('DataSourcePicker', () => {
describe('onClear', () => {
it('should call onClear when function is passed', async () => {
const onClear = jest.fn();
const select = render(<DataSourcePicker onClear={onClear} />);
const clearButton = select.getByLabelText('select-clear-value');
userEvent.click(clearButton);
expect(onClear).toHaveBeenCalled();
});
it('should not render clear button when no onClear function is passed', async () => {
const select = render(<DataSourcePicker />);
expect(() => {
select.getByLabelText('select-clear-value');
}).toThrowError();
});
});
});

View File

@@ -2,7 +2,7 @@
import React, { PureComponent } from 'react';
// Components
import { HorizontalGroup, PluginSignatureBadge, Select, stylesFactory } from '@grafana/ui';
import { ActionMeta, HorizontalGroup, PluginSignatureBadge, Select, stylesFactory } from '@grafana/ui';
import {
DataSourceInstanceSettings,
DataSourceRef,
@@ -40,6 +40,7 @@ export interface DataSourcePickerProps {
noDefault?: boolean;
width?: number;
filter?: (dataSource: DataSourceInstanceSettings) => boolean;
onClear?: () => void;
}
/**
@@ -80,7 +81,12 @@ export class DataSourcePicker extends PureComponent<DataSourcePickerProps, DataS
}
}
onChange = (item: SelectableValue<string>) => {
onChange = (item: SelectableValue<string>, actionMeta: ActionMeta) => {
if (actionMeta.action === 'clear' && this.props.onClear) {
this.props.onClear();
return;
}
const dsSettings = this.dataSourceSrv.getInstanceSettings(item.value);
if (dsSettings) {
@@ -142,11 +148,12 @@ export class DataSourcePicker extends PureComponent<DataSourcePickerProps, DataS
}
render() {
const { autoFocus, onBlur, openMenuOnFocus, placeholder, width } = this.props;
const { autoFocus, onBlur, onClear, openMenuOnFocus, placeholder, width } = this.props;
const { error } = this.state;
const options = this.getDataSourceOptions();
const value = this.getCurrentValue();
const styles = getStyles();
const isClearable = typeof onClear === 'function';
return (
<div aria-label={selectors.components.DataSourcePicker.container}>
@@ -156,7 +163,7 @@ export class DataSourcePicker extends PureComponent<DataSourcePickerProps, DataS
menuShouldPortal
className={styles.select}
isMulti={false}
isClearable={false}
isClearable={isClearable}
backspaceRemovesValue={false}
onChange={this.onChange}
options={options}

View File

@@ -0,0 +1,22 @@
const ds1 = {
id: 1,
uid: 'c8eceabb-0275-4108-8f03-8f74faf4bf6d',
type: 'prometheus',
name: 'gdev-prometheus',
meta: {
info: {
logos: {
small: 'http://example.com/logo.png',
},
},
},
jsonData: {},
access: 'proxy',
};
export function getDataSourceSrv() {
return {
getList: () => [ds1],
getInstanceSettings: () => ds1,
};
}

View File

@@ -202,10 +202,10 @@ describe('SelectBase', () => {
expect(selectEl).toBeInTheDocument();
await selectOptionInTest(selectEl, 'Option 2');
expect(spy).toHaveBeenCalledWith({
label: 'Option 2',
value: 2,
});
expect(spy).toHaveBeenCalledWith(
{ label: 'Option 2', value: 2 },
{ action: 'select-option', name: undefined, option: undefined }
);
});
});
});

View File

@@ -20,7 +20,7 @@ import { MultiValueContainer, MultiValueRemove } from './MultiValue';
import { useTheme2 } from '../../themes';
import { getSelectStyles } from './getSelectStyles';
import { cleanValue, findSelectedValue } from './utils';
import { SelectBaseProps, SelectValue } from './types';
import { ActionMeta, SelectBaseProps, SelectValue } from './types';
import { deprecationWarning } from '@grafana/data';
interface ExtraValuesIndicatorProps {
@@ -146,11 +146,11 @@ export function SelectBase<T>({
const theme = useTheme2();
const styles = getSelectStyles(theme);
const onChangeWithEmpty = useCallback(
(value: SelectValue<T>) => {
(value: SelectValue<T>, action: ActionMeta) => {
if (isMulti && (value === undefined || value === null)) {
return onChange([]);
return onChange([], action);
}
onChange(value);
onChange(value, action);
},
[isMulti, onChange]
);

View File

@@ -1,7 +1,9 @@
import { SelectableValue } from '@grafana/data';
import React from 'react';
import { ActionMeta as SelectActionMeta } from 'react-select';
export type SelectValue<T> = T | SelectableValue<T> | T[] | Array<SelectableValue<T>>;
export type ActionMeta = SelectActionMeta<{}>;
export type InputActionMeta = {
action: 'set-value' | 'input-change' | 'input-blur' | 'menu-close';
};
@@ -52,7 +54,7 @@ export interface SelectCommonProps<T> {
/** The message to display when no options could be found */
noOptionsMessage?: string;
onBlur?: () => void;
onChange: (value: SelectableValue<T>) => {} | void;
onChange: (value: SelectableValue<T>, actionMeta: ActionMeta) => {} | void;
onCloseMenu?: () => void;
/** allowCustomValue must be enabled. Function decides what to do with that custom value. */
onCreateOption?: (value: string) => void;

View File

@@ -4,3 +4,4 @@ export * from './completion';
export * from './storybook';
export * from './forms';
export * from './icon';
export * from './select';

View File

@@ -0,0 +1 @@
export { ActionMeta } from '../components/Select/types';