mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
NewPanelEdit: Remove legacy editors for thresholds, value mappings and field properties (#23497)
* Remove legacy value mappings editor * Remove FieldPropertiesEditor * Remove legacy ThresholdsEditor
This commit is contained in:
@@ -1,32 +0,0 @@
|
||||
import React from 'react';
|
||||
import { FieldPropertiesEditor } from './FieldPropertiesEditor';
|
||||
import { FieldConfig } from '@grafana/data';
|
||||
import { mount } from 'enzyme';
|
||||
|
||||
describe('FieldPropertiesEditor', () => {
|
||||
describe('when bluring min/max field', () => {
|
||||
it("when title was modified it should persist it's value", () => {
|
||||
const onChangeHandler = jest.fn();
|
||||
const value: FieldConfig = {
|
||||
title: 'Title set',
|
||||
};
|
||||
const container = mount(<FieldPropertiesEditor value={value} onChange={onChangeHandler} showTitle showMinMax />);
|
||||
const minInput = container.find('input[aria-label="Field properties editor min input"]');
|
||||
const maxInput = container.find('input[aria-label="Field properties editor max input"]');
|
||||
|
||||
// Simulating title update provided from PanelModel options
|
||||
container.setProps({ value: { title: 'Title updated' } });
|
||||
|
||||
minInput.simulate('blur');
|
||||
maxInput.simulate('blur');
|
||||
|
||||
expect(onChangeHandler).toHaveBeenLastCalledWith({
|
||||
title: 'Title updated',
|
||||
min: undefined,
|
||||
max: undefined,
|
||||
decimals: undefined,
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
//
|
||||
@@ -1,142 +0,0 @@
|
||||
// Libraries
|
||||
import React, { ChangeEvent, useState, useCallback } from 'react';
|
||||
|
||||
// Components
|
||||
import { FormField } from '../FormField/FormField';
|
||||
import { FormLabel } from '../FormLabel/FormLabel';
|
||||
import { UnitPicker } from '../UnitPicker/UnitPicker';
|
||||
|
||||
// Types
|
||||
import {
|
||||
VAR_SERIES_NAME,
|
||||
VAR_FIELD_NAME,
|
||||
VAR_CALC,
|
||||
VAR_CELL_PREFIX,
|
||||
toIntegerOrUndefined,
|
||||
FieldConfig,
|
||||
toFloatOrUndefined,
|
||||
toNumberString,
|
||||
} from '@grafana/data';
|
||||
|
||||
const labelWidth = 6;
|
||||
|
||||
export interface Props {
|
||||
showMinMax?: boolean;
|
||||
showTitle?: boolean;
|
||||
value: FieldConfig;
|
||||
onChange: (value: FieldConfig, event?: React.SyntheticEvent<HTMLElement>) => void;
|
||||
}
|
||||
|
||||
export const FieldPropertiesEditor: React.FC<Props> = ({ value, onChange, showMinMax, showTitle }) => {
|
||||
const { unit, title } = value;
|
||||
|
||||
const [decimals, setDecimals] = useState(
|
||||
value.decimals !== undefined && value.decimals !== null ? value.decimals.toString() : ''
|
||||
);
|
||||
const [min, setMin] = useState(toNumberString(value.min));
|
||||
const [max, setMax] = useState(toNumberString(value.max));
|
||||
|
||||
const onTitleChange = (event: ChangeEvent<HTMLInputElement>) => {
|
||||
onChange({ ...value, title: event.target.value });
|
||||
};
|
||||
|
||||
const onDecimalChange = useCallback(
|
||||
(event: ChangeEvent<HTMLInputElement>) => {
|
||||
setDecimals(event.target.value);
|
||||
},
|
||||
[value.decimals, onChange]
|
||||
);
|
||||
|
||||
const onMinChange = useCallback(
|
||||
(event: ChangeEvent<HTMLInputElement>) => {
|
||||
setMin(event.target.value);
|
||||
},
|
||||
[value.min, onChange]
|
||||
);
|
||||
|
||||
const onMaxChange = useCallback(
|
||||
(event: ChangeEvent<HTMLInputElement>) => {
|
||||
setMax(event.target.value);
|
||||
},
|
||||
[value.max, onChange]
|
||||
);
|
||||
|
||||
const onUnitChange = (unit?: string) => {
|
||||
onChange({ ...value, unit });
|
||||
};
|
||||
|
||||
const commitChanges = useCallback(() => {
|
||||
onChange({
|
||||
...value,
|
||||
decimals: toIntegerOrUndefined(decimals),
|
||||
min: toFloatOrUndefined(min),
|
||||
max: toFloatOrUndefined(max),
|
||||
});
|
||||
}, [min, max, decimals, value]);
|
||||
|
||||
const titleTooltip = (
|
||||
<div>
|
||||
Template Variables:
|
||||
<br />
|
||||
{'${' + VAR_SERIES_NAME + '}'}
|
||||
<br />
|
||||
{'${' + VAR_FIELD_NAME + '}'}
|
||||
<br />
|
||||
{'$' + VAR_CELL_PREFIX + '{N}'} / {'$' + VAR_CALC}
|
||||
</div>
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
{showTitle && (
|
||||
<FormField
|
||||
label="Title"
|
||||
labelWidth={labelWidth}
|
||||
onChange={onTitleChange}
|
||||
value={title}
|
||||
tooltip={titleTooltip}
|
||||
placeholder="Auto"
|
||||
aria-label="Field properties editor title input"
|
||||
/>
|
||||
)}
|
||||
|
||||
<div className="gf-form">
|
||||
<FormLabel width={labelWidth}>Unit</FormLabel>
|
||||
<UnitPicker value={unit} onChange={onUnitChange} />
|
||||
</div>
|
||||
{showMinMax && (
|
||||
<>
|
||||
<FormField
|
||||
label="Min"
|
||||
labelWidth={labelWidth}
|
||||
onChange={onMinChange}
|
||||
onBlur={commitChanges}
|
||||
value={min}
|
||||
placeholder="Auto"
|
||||
type="number"
|
||||
aria-label="Field properties editor min input"
|
||||
/>
|
||||
<FormField
|
||||
label="Max"
|
||||
labelWidth={labelWidth}
|
||||
onChange={onMaxChange}
|
||||
onBlur={commitChanges}
|
||||
value={max}
|
||||
type="number"
|
||||
placeholder="Auto"
|
||||
aria-label="Field properties editor max input"
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
<FormField
|
||||
label="Decimals"
|
||||
labelWidth={labelWidth}
|
||||
placeholder="auto"
|
||||
onChange={onDecimalChange}
|
||||
onBlur={commitChanges}
|
||||
value={decimals}
|
||||
type="number"
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
@@ -1,5 +1,4 @@
|
||||
export { FieldDisplayEditor } from './FieldDisplayEditor';
|
||||
export { FieldPropertiesEditor } from './FieldPropertiesEditor';
|
||||
|
||||
export {
|
||||
SingleStatBaseOptions,
|
||||
|
||||
@@ -1,29 +0,0 @@
|
||||
import React from 'react';
|
||||
import { action } from '@storybook/addon-actions';
|
||||
import { object } from '@storybook/addon-knobs';
|
||||
|
||||
import { ThresholdsEditor } from './ThresholdsEditor';
|
||||
import { ThresholdsMode } from '@grafana/data';
|
||||
|
||||
export default {
|
||||
title: 'Panel/ThresholdsEditor',
|
||||
component: ThresholdsEditor,
|
||||
};
|
||||
|
||||
const getKnobs = () => {
|
||||
return {
|
||||
initThresholds: object('Initial thresholds', {
|
||||
mode: ThresholdsMode.Absolute,
|
||||
steps: [
|
||||
{ value: -Infinity, color: 'green' },
|
||||
{ value: 50, color: 'red' },
|
||||
],
|
||||
}),
|
||||
};
|
||||
};
|
||||
|
||||
export const basic = () => <ThresholdsEditor onChange={action('Thresholds changed')} />;
|
||||
|
||||
export const withThresholds = () => (
|
||||
<ThresholdsEditor thresholds={getKnobs().initThresholds} onChange={action('Thresholds changed')} />
|
||||
);
|
||||
@@ -1,214 +0,0 @@
|
||||
import React, { ChangeEvent } from 'react';
|
||||
import { mount } from 'enzyme';
|
||||
import { GrafanaThemeType, ThresholdsMode } from '@grafana/data';
|
||||
import { ThresholdsEditor, Props, thresholdsWithoutKey } from './ThresholdsEditor';
|
||||
import { colors } from '../../utils/colors';
|
||||
import { mockThemeContext } from '../../themes/ThemeContext';
|
||||
|
||||
const setup = (propOverrides?: Partial<Props>) => {
|
||||
const props: Props = {
|
||||
onChange: jest.fn(),
|
||||
thresholds: { mode: ThresholdsMode.Absolute, steps: [] },
|
||||
};
|
||||
|
||||
Object.assign(props, propOverrides);
|
||||
|
||||
const wrapper = mount(<ThresholdsEditor {...props} />);
|
||||
const instance = wrapper.instance() as ThresholdsEditor;
|
||||
|
||||
return {
|
||||
instance,
|
||||
wrapper,
|
||||
};
|
||||
};
|
||||
|
||||
function getCurrentThresholds(editor: ThresholdsEditor) {
|
||||
return thresholdsWithoutKey(editor.props.thresholds, editor.state.steps);
|
||||
}
|
||||
|
||||
describe('Render', () => {
|
||||
let restoreThemeContext: any;
|
||||
beforeAll(() => {
|
||||
restoreThemeContext = mockThemeContext({ type: GrafanaThemeType.Dark });
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
restoreThemeContext();
|
||||
});
|
||||
|
||||
it('should render with base threshold', () => {
|
||||
const { wrapper } = setup();
|
||||
expect(wrapper.find('.thresholds')).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
||||
describe('Initialization', () => {
|
||||
it('should add a base threshold if missing', () => {
|
||||
const { instance } = setup();
|
||||
expect(getCurrentThresholds(instance).steps).toEqual([{ value: -Infinity, color: 'green' }]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Add threshold', () => {
|
||||
it('should add threshold', () => {
|
||||
const { instance } = setup();
|
||||
|
||||
instance.onAddThresholdAfter(instance.state.steps[0]);
|
||||
|
||||
expect(getCurrentThresholds(instance).steps).toEqual([
|
||||
{ value: -Infinity, color: 'green' }, // 0
|
||||
{ value: 50, color: colors[1] }, // 1
|
||||
]);
|
||||
});
|
||||
|
||||
it('should add another threshold above a first', () => {
|
||||
const { instance } = setup({
|
||||
thresholds: {
|
||||
mode: ThresholdsMode.Absolute,
|
||||
steps: [
|
||||
{ value: -Infinity, color: colors[0] }, // 0
|
||||
{ value: 50, color: colors[2] }, // 1
|
||||
],
|
||||
},
|
||||
});
|
||||
|
||||
instance.onAddThresholdAfter(instance.state.steps[1]);
|
||||
|
||||
expect(getCurrentThresholds(instance).steps).toEqual([
|
||||
{ value: -Infinity, color: colors[0] }, // 0
|
||||
{ value: 50, color: colors[2] }, // 1
|
||||
{ value: 75, color: colors[3] }, // 2
|
||||
]);
|
||||
});
|
||||
|
||||
it('should add another threshold between first and second index', () => {
|
||||
const { instance } = setup({
|
||||
thresholds: {
|
||||
mode: ThresholdsMode.Absolute,
|
||||
steps: [
|
||||
{ value: -Infinity, color: colors[0] },
|
||||
{ value: 50, color: colors[2] },
|
||||
{ value: 75, color: colors[3] },
|
||||
],
|
||||
},
|
||||
});
|
||||
|
||||
instance.onAddThresholdAfter(instance.state.steps[1]);
|
||||
|
||||
expect(getCurrentThresholds(instance).steps).toEqual([
|
||||
{ value: -Infinity, color: colors[0] },
|
||||
{ value: 50, color: colors[2] },
|
||||
{ value: 62.5, color: colors[4] },
|
||||
{ value: 75, color: colors[3] },
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Remove threshold', () => {
|
||||
it('should not remove threshold at index 0', () => {
|
||||
const thresholds = {
|
||||
mode: ThresholdsMode.Absolute,
|
||||
steps: [
|
||||
{ value: -Infinity, color: '#7EB26D' },
|
||||
{ value: 50, color: '#EAB839' },
|
||||
{ value: 75, color: '#6ED0E0' },
|
||||
],
|
||||
};
|
||||
const { instance } = setup({ thresholds });
|
||||
|
||||
instance.onRemoveThreshold(instance.state.steps[0]);
|
||||
|
||||
expect(getCurrentThresholds(instance)).toEqual(thresholds);
|
||||
});
|
||||
|
||||
it('should remove threshold', () => {
|
||||
const thresholds = {
|
||||
mode: ThresholdsMode.Absolute,
|
||||
steps: [
|
||||
{ value: -Infinity, color: '#7EB26D' },
|
||||
{ value: 50, color: '#EAB839' },
|
||||
{ value: 75, color: '#6ED0E0' },
|
||||
],
|
||||
};
|
||||
const { instance } = setup({ thresholds });
|
||||
|
||||
instance.onRemoveThreshold(instance.state.steps[1]);
|
||||
|
||||
expect(getCurrentThresholds(instance).steps).toEqual([
|
||||
{ value: -Infinity, color: '#7EB26D' },
|
||||
{ value: 75, color: '#6ED0E0' },
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('change threshold value', () => {
|
||||
it('should not change threshold at index 0', () => {
|
||||
const thresholds = {
|
||||
mode: ThresholdsMode.Absolute,
|
||||
steps: [
|
||||
{ value: -Infinity, color: '#7EB26D' },
|
||||
{ value: 50, color: '#EAB839' },
|
||||
{ value: 75, color: '#6ED0E0' },
|
||||
],
|
||||
};
|
||||
const { instance } = setup({ thresholds });
|
||||
|
||||
const mockEvent = ({ target: { value: '12' } } as any) as ChangeEvent<HTMLInputElement>;
|
||||
|
||||
instance.onChangeThresholdValue(mockEvent, instance.state.steps[0]);
|
||||
|
||||
expect(getCurrentThresholds(instance)).toEqual(thresholds);
|
||||
});
|
||||
|
||||
it('should update value', () => {
|
||||
const { instance } = setup();
|
||||
const thresholds = {
|
||||
mode: ThresholdsMode.Absolute,
|
||||
steps: [
|
||||
{ value: -Infinity, color: '#7EB26D', key: 1 },
|
||||
{ value: 50, color: '#EAB839', key: 2 },
|
||||
{ value: 75, color: '#6ED0E0', key: 3 },
|
||||
],
|
||||
};
|
||||
|
||||
instance.state = {
|
||||
steps: thresholds.steps,
|
||||
};
|
||||
|
||||
const mockEvent = ({ target: { value: '78' } } as any) as ChangeEvent<HTMLInputElement>;
|
||||
|
||||
instance.onChangeThresholdValue(mockEvent, thresholds.steps[1]);
|
||||
|
||||
expect(getCurrentThresholds(instance).steps).toEqual([
|
||||
{ value: -Infinity, color: '#7EB26D' },
|
||||
{ value: 78, color: '#EAB839' },
|
||||
{ value: 75, color: '#6ED0E0' },
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('on blur threshold value', () => {
|
||||
it('should resort rows and update indexes', () => {
|
||||
const { instance } = setup();
|
||||
const thresholds = {
|
||||
mode: ThresholdsMode.Absolute,
|
||||
steps: [
|
||||
{ value: -Infinity, color: '#7EB26D', key: 1 },
|
||||
{ value: 78, color: '#EAB839', key: 2 },
|
||||
{ value: 75, color: '#6ED0E0', key: 3 },
|
||||
],
|
||||
};
|
||||
|
||||
instance.setState({
|
||||
steps: thresholds.steps,
|
||||
});
|
||||
|
||||
instance.onBlur();
|
||||
|
||||
expect(getCurrentThresholds(instance).steps).toEqual([
|
||||
{ value: -Infinity, color: '#7EB26D' },
|
||||
{ value: 75, color: '#6ED0E0' },
|
||||
{ value: 78, color: '#EAB839' },
|
||||
]);
|
||||
});
|
||||
});
|
||||
@@ -1,268 +0,0 @@
|
||||
import React, { PureComponent, ChangeEvent } from 'react';
|
||||
import { Threshold, sortThresholds, ThresholdsConfig, ThresholdsMode, SelectableValue } from '@grafana/data';
|
||||
import { colors } from '../../utils';
|
||||
import { getColorFromHexRgbOrName } from '@grafana/data';
|
||||
import { ThemeContext } from '../../themes/ThemeContext';
|
||||
import { Input } from '../Forms/Legacy/Input/Input';
|
||||
import { ColorPicker } from '../ColorPicker/ColorPicker';
|
||||
import { css } from 'emotion';
|
||||
import { PanelOptionsGroup } from '../PanelOptionsGroup/PanelOptionsGroup';
|
||||
|
||||
export interface Props {
|
||||
thresholds?: ThresholdsConfig;
|
||||
onChange: (thresholds: ThresholdsConfig) => void;
|
||||
}
|
||||
|
||||
interface State {
|
||||
steps: ThresholdWithKey[];
|
||||
}
|
||||
|
||||
interface ThresholdWithKey extends Threshold {
|
||||
key: number;
|
||||
}
|
||||
|
||||
let counter = 100;
|
||||
|
||||
export class ThresholdsEditor extends PureComponent<Props, State> {
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
|
||||
const steps = toThresholdsWithKey(props.thresholds);
|
||||
steps[0].value = -Infinity;
|
||||
|
||||
this.state = { steps };
|
||||
}
|
||||
|
||||
onAddThresholdAfter = (threshold: ThresholdWithKey) => {
|
||||
const { steps } = this.state;
|
||||
|
||||
const maxValue = 100;
|
||||
const minValue = 0;
|
||||
|
||||
let prev: ThresholdWithKey | undefined = undefined;
|
||||
let next: ThresholdWithKey | undefined = undefined;
|
||||
for (const t of steps) {
|
||||
if (prev && prev.key === threshold.key) {
|
||||
next = t;
|
||||
break;
|
||||
}
|
||||
prev = t;
|
||||
}
|
||||
|
||||
const prevValue = prev && isFinite(prev.value) ? prev.value : minValue;
|
||||
const nextValue = next && isFinite(next.value) ? next.value : maxValue;
|
||||
|
||||
const color = colors.filter(c => !steps.some(t => t.color === c))[1];
|
||||
const add = {
|
||||
value: prevValue + (nextValue - prevValue) / 2.0,
|
||||
color: color,
|
||||
key: counter++,
|
||||
};
|
||||
const newThresholds = [...steps, add];
|
||||
sortThresholds(newThresholds);
|
||||
|
||||
this.setState(
|
||||
{
|
||||
steps: newThresholds,
|
||||
},
|
||||
() => this.onChange()
|
||||
);
|
||||
};
|
||||
|
||||
onRemoveThreshold = (threshold: ThresholdWithKey) => {
|
||||
const { steps } = this.state;
|
||||
if (!steps.length) {
|
||||
return;
|
||||
}
|
||||
// Don't remove index 0
|
||||
if (threshold.key === steps[0].key) {
|
||||
return;
|
||||
}
|
||||
this.setState(
|
||||
{
|
||||
steps: steps.filter(t => t.key !== threshold.key),
|
||||
},
|
||||
() => this.onChange()
|
||||
);
|
||||
};
|
||||
|
||||
onChangeThresholdValue = (event: ChangeEvent<HTMLInputElement>, threshold: ThresholdWithKey) => {
|
||||
const cleanValue = event.target.value.replace(/,/g, '.');
|
||||
const parsedValue = parseFloat(cleanValue);
|
||||
const value = isNaN(parsedValue) ? '' : parsedValue;
|
||||
|
||||
const steps = this.state.steps.map(t => {
|
||||
if (t.key === threshold.key) {
|
||||
t = { ...t, value: value as number };
|
||||
}
|
||||
return t;
|
||||
});
|
||||
if (steps.length) {
|
||||
steps[0].value = -Infinity;
|
||||
}
|
||||
this.setState({ steps });
|
||||
};
|
||||
|
||||
onChangeThresholdColor = (threshold: ThresholdWithKey, color: string) => {
|
||||
const { steps } = this.state;
|
||||
|
||||
const newThresholds = steps.map(t => {
|
||||
if (t.key === threshold.key) {
|
||||
t = { ...t, color: color };
|
||||
}
|
||||
|
||||
return t;
|
||||
});
|
||||
|
||||
this.setState(
|
||||
{
|
||||
steps: newThresholds,
|
||||
},
|
||||
() => this.onChange()
|
||||
);
|
||||
};
|
||||
|
||||
onBlur = () => {
|
||||
const steps = [...this.state.steps];
|
||||
sortThresholds(steps);
|
||||
this.setState(
|
||||
{
|
||||
steps,
|
||||
},
|
||||
() => this.onChange()
|
||||
);
|
||||
};
|
||||
|
||||
onChange = () => {
|
||||
this.props.onChange(thresholdsWithoutKey(this.props.thresholds, this.state.steps));
|
||||
};
|
||||
|
||||
onModeChanged = (item: SelectableValue<ThresholdsMode>) => {
|
||||
if (item.value) {
|
||||
this.props.onChange({
|
||||
...getThresholdOrDefault(this.props.thresholds),
|
||||
mode: item.value,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
renderInput = (threshold: ThresholdWithKey) => {
|
||||
const config = getThresholdOrDefault(this.props.thresholds);
|
||||
const isPercent = config.mode === ThresholdsMode.Percentage;
|
||||
|
||||
return (
|
||||
<div className="thresholds-row-input-inner">
|
||||
<span className="thresholds-row-input-inner-arrow" />
|
||||
<div className="thresholds-row-input-inner-color">
|
||||
{threshold.color && (
|
||||
<div className="thresholds-row-input-inner-color-colorpicker">
|
||||
<ColorPicker
|
||||
color={threshold.color}
|
||||
onChange={color => this.onChangeThresholdColor(threshold, color)}
|
||||
enableNamedColors={true}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
{!isFinite(threshold.value) ? (
|
||||
<div className="thresholds-row-input-inner-value">
|
||||
<Input type="text" value="Base" readOnly />
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
<div className="thresholds-row-input-inner-value">
|
||||
<Input
|
||||
type="number"
|
||||
step="0.0001"
|
||||
onChange={(event: ChangeEvent<HTMLInputElement>) => this.onChangeThresholdValue(event, threshold)}
|
||||
value={threshold.value}
|
||||
onBlur={this.onBlur}
|
||||
/>
|
||||
</div>
|
||||
{isPercent && (
|
||||
<div className={css(`margin-left:-20px; margin-top:5px;`)}>
|
||||
<i className="fa fa-percent" />
|
||||
</div>
|
||||
)}
|
||||
<div className="thresholds-row-input-inner-remove" onClick={() => this.onRemoveThreshold(threshold)}>
|
||||
<i className="fa fa-times" />
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
render() {
|
||||
const { steps } = this.state;
|
||||
|
||||
return (
|
||||
<PanelOptionsGroup title="Thresholds">
|
||||
<ThemeContext.Consumer>
|
||||
{theme => (
|
||||
<>
|
||||
<div className="thresholds">
|
||||
{steps
|
||||
.slice(0)
|
||||
.reverse()
|
||||
.map(threshold => {
|
||||
return (
|
||||
<div className="thresholds-row" key={`${threshold.key}`}>
|
||||
<div className="thresholds-row-add-button" onClick={() => this.onAddThresholdAfter(threshold)}>
|
||||
<i className="fa fa-plus" />
|
||||
</div>
|
||||
<div
|
||||
className="thresholds-row-color-indicator"
|
||||
style={{ backgroundColor: getColorFromHexRgbOrName(threshold.color, theme.type) }}
|
||||
/>
|
||||
<div className="thresholds-row-input">{this.renderInput(threshold)}</div>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</ThemeContext.Consumer>
|
||||
</PanelOptionsGroup>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export function thresholdsWithoutKey(
|
||||
thresholds: ThresholdsConfig | undefined,
|
||||
steps: ThresholdWithKey[]
|
||||
): ThresholdsConfig {
|
||||
thresholds = getThresholdOrDefault(thresholds);
|
||||
|
||||
const mode = thresholds.mode ?? ThresholdsMode.Absolute;
|
||||
|
||||
return {
|
||||
mode,
|
||||
steps: steps.map(t => {
|
||||
const { key, ...rest } = t;
|
||||
return rest; // everything except key
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
function getThresholdOrDefault(thresholds?: ThresholdsConfig): ThresholdsConfig {
|
||||
return thresholds ?? { steps: [], mode: ThresholdsMode.Absolute };
|
||||
}
|
||||
|
||||
function toThresholdsWithKey(thresholds?: ThresholdsConfig): ThresholdWithKey[] {
|
||||
thresholds = getThresholdOrDefault(thresholds);
|
||||
|
||||
let steps: Threshold[] = thresholds.steps || [];
|
||||
|
||||
if (thresholds.steps && thresholds.steps.length === 0) {
|
||||
steps = [{ value: -Infinity, color: 'green' }];
|
||||
}
|
||||
|
||||
return steps.map(t => {
|
||||
return {
|
||||
color: t.color,
|
||||
value: t.value === null ? -Infinity : t.value,
|
||||
key: counter++,
|
||||
};
|
||||
});
|
||||
}
|
||||
@@ -1,106 +0,0 @@
|
||||
.thresholds {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.thresholds-row {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
height: 62px;
|
||||
}
|
||||
|
||||
.thresholds-row:first-child > .thresholds-row-color-indicator {
|
||||
border-top-left-radius: $border-radius;
|
||||
border-top-right-radius: $border-radius;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.thresholds-row:last-child > .thresholds-row-color-indicator {
|
||||
border-bottom-left-radius: $border-radius;
|
||||
border-bottom-right-radius: $border-radius;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.thresholds-row-add-button {
|
||||
@include buttonBackground($btn-success-bg, $btn-success-bg-hl, #fff);
|
||||
|
||||
align-self: center;
|
||||
margin-right: 5px;
|
||||
height: 24px;
|
||||
width: 24px;
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-shrink: 0;
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
color: $white;
|
||||
}
|
||||
}
|
||||
|
||||
.thresholds-row-color-indicator {
|
||||
width: 10px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.thresholds-row-input {
|
||||
margin-top: 44px;
|
||||
margin-left: 2px;
|
||||
}
|
||||
|
||||
.thresholds-row-input-inner {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
.thresholds-row-input-inner > *:last-child {
|
||||
border-top-right-radius: $border-radius;
|
||||
border-bottom-right-radius: $border-radius;
|
||||
}
|
||||
|
||||
.thresholds-row-input-inner-arrow {
|
||||
align-self: center;
|
||||
width: 0;
|
||||
height: 0;
|
||||
border-top: 6px solid transparent;
|
||||
border-bottom: 6px solid transparent;
|
||||
border-right: 6px solid $input-label-border-color;
|
||||
}
|
||||
|
||||
.thresholds-row-input-inner-value > input {
|
||||
height: $input-height;
|
||||
padding: $input-padding;
|
||||
width: 150px;
|
||||
border-top: 1px solid $input-label-border-color;
|
||||
border-bottom: 1px solid $input-label-border-color;
|
||||
}
|
||||
|
||||
.thresholds-row-input-inner-color {
|
||||
width: 42px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background-color: $input-bg;
|
||||
border: 1px solid $input-label-border-color;
|
||||
}
|
||||
|
||||
.thresholds-row-input-inner-color-colorpicker {
|
||||
border-radius: 10px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.25);
|
||||
}
|
||||
|
||||
.thresholds-row-input-inner-remove {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: $input-height;
|
||||
padding: $input-padding;
|
||||
width: 42px;
|
||||
background-color: $input-label-bg;
|
||||
border: 1px solid $input-label-border-color;
|
||||
cursor: pointer;
|
||||
}
|
||||
@@ -1,156 +0,0 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`Render should render with base threshold 1`] = `
|
||||
<div
|
||||
className="thresholds"
|
||||
>
|
||||
<div
|
||||
className="thresholds-row"
|
||||
key="100"
|
||||
>
|
||||
<div
|
||||
className="thresholds-row-add-button"
|
||||
onClick={[Function]}
|
||||
>
|
||||
<i
|
||||
className="fa fa-plus"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
className="thresholds-row-color-indicator"
|
||||
style={
|
||||
Object {
|
||||
"backgroundColor": "#73BF69",
|
||||
}
|
||||
}
|
||||
/>
|
||||
<div
|
||||
className="thresholds-row-input"
|
||||
>
|
||||
<div
|
||||
className="thresholds-row-input-inner"
|
||||
>
|
||||
<span
|
||||
className="thresholds-row-input-inner-arrow"
|
||||
/>
|
||||
<div
|
||||
className="thresholds-row-input-inner-color"
|
||||
>
|
||||
<div
|
||||
className="thresholds-row-input-inner-color-colorpicker"
|
||||
>
|
||||
<WithTheme(ColorPicker)
|
||||
color="green"
|
||||
enableNamedColors={true}
|
||||
onChange={[Function]}
|
||||
>
|
||||
<ColorPicker
|
||||
color="green"
|
||||
enableNamedColors={true}
|
||||
onChange={[Function]}
|
||||
theme={
|
||||
Object {
|
||||
"type": "dark",
|
||||
}
|
||||
}
|
||||
>
|
||||
<PopoverController
|
||||
content={
|
||||
<ColorPickerPopover
|
||||
color="green"
|
||||
enableNamedColors={true}
|
||||
onChange={[Function]}
|
||||
theme={
|
||||
Object {
|
||||
"type": "dark",
|
||||
}
|
||||
}
|
||||
/>
|
||||
}
|
||||
hideAfter={300}
|
||||
>
|
||||
<ForwardRef(ColorPickerTrigger)
|
||||
color="#73BF69"
|
||||
onClick={[Function]}
|
||||
onMouseLeave={[Function]}
|
||||
>
|
||||
<div
|
||||
onClick={[Function]}
|
||||
onMouseLeave={[Function]}
|
||||
style={
|
||||
Object {
|
||||
"background": "inherit",
|
||||
"border": "none",
|
||||
"borderRadius": 10,
|
||||
"color": "inherit",
|
||||
"cursor": "pointer",
|
||||
"overflow": "hidden",
|
||||
"padding": 0,
|
||||
}
|
||||
}
|
||||
>
|
||||
<div
|
||||
style={
|
||||
Object {
|
||||
"backgroundImage": "url(data:image/png,base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAAMCAIAAADZF8uwAAAAGUlEQVQYV2M4gwH+YwCGIasIUwhT25BVBADtzYNYrHvv4gAAAABJRU5ErkJggg==)",
|
||||
"border": "none",
|
||||
"float": "left",
|
||||
"height": 15,
|
||||
"margin": 0,
|
||||
"position": "relative",
|
||||
"width": 15,
|
||||
"zIndex": 0,
|
||||
}
|
||||
}
|
||||
>
|
||||
<div
|
||||
style={
|
||||
Object {
|
||||
"backgroundColor": "#73BF69",
|
||||
"bottom": 0,
|
||||
"display": "block",
|
||||
"left": 0,
|
||||
"position": "absolute",
|
||||
"right": 0,
|
||||
"top": 0,
|
||||
}
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</ForwardRef(ColorPickerTrigger)>
|
||||
</PopoverController>
|
||||
</ColorPicker>
|
||||
</WithTheme(ColorPicker)>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className="thresholds-row-input-inner-value"
|
||||
>
|
||||
<Input
|
||||
className=""
|
||||
readOnly={true}
|
||||
type="text"
|
||||
value="Base"
|
||||
>
|
||||
<div
|
||||
style={
|
||||
Object {
|
||||
"flexGrow": 1,
|
||||
}
|
||||
}
|
||||
>
|
||||
<input
|
||||
className="gf-form-input"
|
||||
readOnly={true}
|
||||
type="text"
|
||||
value="Base"
|
||||
/>
|
||||
</div>
|
||||
</Input>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
@@ -1,146 +0,0 @@
|
||||
import React, { ChangeEvent, PureComponent } from 'react';
|
||||
|
||||
import { FormField } from '../FormField/FormField';
|
||||
import { FormLabel } from '../FormLabel/FormLabel';
|
||||
import { Input } from '../Forms/Legacy/Input/Input';
|
||||
import { Select } from '../Forms/Legacy/Select/Select';
|
||||
|
||||
import { MappingType, ValueMapping } from '@grafana/data';
|
||||
|
||||
export interface Props {
|
||||
valueMapping: ValueMapping;
|
||||
updateValueMapping: (valueMapping: ValueMapping) => void;
|
||||
removeValueMapping: () => void;
|
||||
}
|
||||
|
||||
interface State {
|
||||
from?: string;
|
||||
id: number;
|
||||
operator: string;
|
||||
text: string;
|
||||
to?: string;
|
||||
type: MappingType;
|
||||
value?: string;
|
||||
}
|
||||
|
||||
const mappingOptions = [
|
||||
{ value: MappingType.ValueToText, label: 'Value' },
|
||||
{ value: MappingType.RangeToText, label: 'Range' },
|
||||
];
|
||||
|
||||
export default class LegacyMappingRow extends PureComponent<Props, State> {
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
|
||||
this.state = { ...props.valueMapping };
|
||||
}
|
||||
|
||||
onMappingValueChange = (event: ChangeEvent<HTMLInputElement>) => {
|
||||
this.setState({ value: event.target.value });
|
||||
};
|
||||
|
||||
onMappingFromChange = (event: ChangeEvent<HTMLInputElement>) => {
|
||||
this.setState({ from: event.target.value });
|
||||
};
|
||||
|
||||
onMappingToChange = (event: ChangeEvent<HTMLInputElement>) => {
|
||||
this.setState({ to: event.target.value });
|
||||
};
|
||||
|
||||
onMappingTextChange = (event: ChangeEvent<HTMLInputElement>) => {
|
||||
this.setState({ text: event.target.value });
|
||||
};
|
||||
|
||||
onMappingTypeChange = (mappingType: MappingType) => {
|
||||
this.setState({ type: mappingType });
|
||||
};
|
||||
|
||||
updateMapping = () => {
|
||||
this.props.updateValueMapping({ ...this.state } as ValueMapping);
|
||||
};
|
||||
|
||||
renderRow() {
|
||||
const { from, text, to, type, value } = this.state;
|
||||
|
||||
if (type === MappingType.RangeToText) {
|
||||
return (
|
||||
<>
|
||||
<FormField
|
||||
label="From"
|
||||
labelWidth={4}
|
||||
inputWidth={8}
|
||||
onBlur={this.updateMapping}
|
||||
onChange={this.onMappingFromChange}
|
||||
value={from}
|
||||
/>
|
||||
<FormField
|
||||
label="To"
|
||||
labelWidth={4}
|
||||
inputWidth={8}
|
||||
onBlur={this.updateMapping}
|
||||
onChange={this.onMappingToChange}
|
||||
value={to}
|
||||
/>
|
||||
<div className="gf-form gf-form--grow">
|
||||
<FormLabel width={4}>Text</FormLabel>
|
||||
<Input
|
||||
className="gf-form-input"
|
||||
onBlur={this.updateMapping}
|
||||
value={text}
|
||||
onChange={this.onMappingTextChange}
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<FormField
|
||||
label="Value"
|
||||
labelWidth={4}
|
||||
onBlur={this.updateMapping}
|
||||
onChange={this.onMappingValueChange}
|
||||
value={value}
|
||||
inputWidth={8}
|
||||
/>
|
||||
<div className="gf-form gf-form--grow">
|
||||
<FormLabel width={4}>Text</FormLabel>
|
||||
<Input
|
||||
className="gf-form-input"
|
||||
onBlur={this.updateMapping}
|
||||
value={text}
|
||||
onChange={this.onMappingTextChange}
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
const { type } = this.state;
|
||||
|
||||
return (
|
||||
<div className="gf-form-inline">
|
||||
<div className="gf-form">
|
||||
<FormLabel width={5}>Type</FormLabel>
|
||||
<Select
|
||||
placeholder="Choose type"
|
||||
isSearchable={false}
|
||||
options={mappingOptions}
|
||||
value={mappingOptions.find(o => o.value === type)}
|
||||
// @ts-ignore
|
||||
onChange={type => this.onMappingTypeChange(type.value)}
|
||||
width={7}
|
||||
/>
|
||||
</div>
|
||||
{this.renderRow()}
|
||||
<div className="gf-form">
|
||||
<button onClick={this.props.removeValueMapping} className="gf-form-label gf-form-label--btn">
|
||||
<i className="fa fa-times" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
import React from 'react';
|
||||
import { storiesOf } from '@storybook/react';
|
||||
import { action } from '@storybook/addon-actions';
|
||||
import { LegacyValueMappingsEditor } from './LegacyValueMappingsEditor';
|
||||
|
||||
const ValueMappingsEditorStories = storiesOf('Panel/LegacyValueMappingsEditor', module);
|
||||
|
||||
ValueMappingsEditorStories.add('default', () => {
|
||||
return <LegacyValueMappingsEditor valueMappings={[]} onChange={action('Mapping changed')} />;
|
||||
});
|
||||
@@ -1,72 +0,0 @@
|
||||
import React from 'react';
|
||||
import { shallow } from 'enzyme';
|
||||
|
||||
import { LegacyValueMappingsEditor, Props } from './LegacyValueMappingsEditor';
|
||||
import { MappingType } from '@grafana/data';
|
||||
|
||||
const setup = (propOverrides?: object) => {
|
||||
const props: Props = {
|
||||
onChange: jest.fn(),
|
||||
valueMappings: [
|
||||
{ id: 1, operator: '', type: MappingType.ValueToText, value: '20', text: 'Ok' },
|
||||
{ id: 2, operator: '', type: MappingType.RangeToText, from: '21', to: '30', text: 'Meh' },
|
||||
],
|
||||
};
|
||||
|
||||
Object.assign(props, propOverrides);
|
||||
|
||||
const wrapper = shallow(<LegacyValueMappingsEditor {...props} />);
|
||||
|
||||
const instance = wrapper.instance() as LegacyValueMappingsEditor;
|
||||
|
||||
return {
|
||||
instance,
|
||||
wrapper,
|
||||
};
|
||||
};
|
||||
|
||||
describe('Render', () => {
|
||||
it('should render component', () => {
|
||||
const { wrapper } = setup();
|
||||
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
||||
describe('On remove mapping', () => {
|
||||
it('Should remove mapping with id 0', () => {
|
||||
const { instance } = setup();
|
||||
|
||||
instance.onRemoveMapping(1);
|
||||
|
||||
expect(instance.state.valueMappings).toEqual([
|
||||
{ id: 2, operator: '', type: MappingType.RangeToText, from: '21', to: '30', text: 'Meh' },
|
||||
]);
|
||||
});
|
||||
|
||||
it('should remove mapping with id 1', () => {
|
||||
const { instance } = setup();
|
||||
|
||||
instance.onRemoveMapping(2);
|
||||
|
||||
expect(instance.state.valueMappings).toEqual([
|
||||
{ id: 1, operator: '', type: MappingType.ValueToText, value: '20', text: 'Ok' },
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Next id to add', () => {
|
||||
it('should be 4', () => {
|
||||
const { instance } = setup();
|
||||
|
||||
instance.onAddMapping();
|
||||
|
||||
expect(instance.state.nextIdToAdd).toEqual(4);
|
||||
});
|
||||
|
||||
it('should default to 1', () => {
|
||||
const { instance } = setup({ valueMappings: [] });
|
||||
|
||||
expect(instance.state.nextIdToAdd).toEqual(1);
|
||||
});
|
||||
});
|
||||
@@ -1,108 +0,0 @@
|
||||
import React, { PureComponent } from 'react';
|
||||
|
||||
import LegacyMappingRow from './LegacyMappingRow';
|
||||
import { MappingType, ValueMapping } from '@grafana/data';
|
||||
import { Button } from '../Button';
|
||||
import { PanelOptionsGroup } from '../PanelOptionsGroup/PanelOptionsGroup';
|
||||
|
||||
export interface Props {
|
||||
valueMappings?: ValueMapping[];
|
||||
onChange: (valueMappings: ValueMapping[]) => void;
|
||||
}
|
||||
|
||||
interface State {
|
||||
valueMappings: ValueMapping[];
|
||||
nextIdToAdd: number;
|
||||
}
|
||||
|
||||
export class LegacyValueMappingsEditor extends PureComponent<Props, State> {
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
|
||||
const mappings = props.valueMappings || [];
|
||||
|
||||
this.state = {
|
||||
valueMappings: mappings,
|
||||
nextIdToAdd: mappings.length > 0 ? this.getMaxIdFromValueMappings(mappings) : 1,
|
||||
};
|
||||
}
|
||||
|
||||
getMaxIdFromValueMappings(mappings: ValueMapping[]) {
|
||||
return (
|
||||
Math.max.apply(
|
||||
null,
|
||||
mappings.map(mapping => mapping.id).map(m => m)
|
||||
) + 1
|
||||
);
|
||||
}
|
||||
|
||||
onAddMapping = () =>
|
||||
this.setState(prevState => ({
|
||||
valueMappings: [
|
||||
...prevState.valueMappings,
|
||||
{
|
||||
id: prevState.nextIdToAdd,
|
||||
operator: '',
|
||||
value: '',
|
||||
text: '',
|
||||
type: MappingType.ValueToText,
|
||||
from: '',
|
||||
to: '',
|
||||
},
|
||||
],
|
||||
nextIdToAdd: prevState.nextIdToAdd + 1,
|
||||
}));
|
||||
|
||||
onRemoveMapping = (id: number) => {
|
||||
this.setState(
|
||||
prevState => ({
|
||||
valueMappings: prevState.valueMappings.filter(m => {
|
||||
return m.id !== id;
|
||||
}),
|
||||
}),
|
||||
() => {
|
||||
this.props.onChange(this.state.valueMappings);
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
updateGauge = (mapping: ValueMapping) => {
|
||||
this.setState(
|
||||
prevState => ({
|
||||
valueMappings: prevState.valueMappings.map(m => {
|
||||
if (m.id === mapping.id) {
|
||||
return { ...mapping };
|
||||
}
|
||||
|
||||
return m;
|
||||
}),
|
||||
}),
|
||||
() => {
|
||||
this.props.onChange(this.state.valueMappings);
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
render() {
|
||||
const { valueMappings } = this.state;
|
||||
|
||||
return (
|
||||
<PanelOptionsGroup title="Value mappings">
|
||||
<div>
|
||||
{valueMappings.length > 0 &&
|
||||
valueMappings.map((valueMapping, index) => (
|
||||
<LegacyMappingRow
|
||||
key={`${valueMapping.text}-${index}`}
|
||||
valueMapping={valueMapping}
|
||||
updateValueMapping={this.updateGauge}
|
||||
removeValueMapping={() => this.onRemoveMapping(valueMapping.id)}
|
||||
/>
|
||||
))}
|
||||
<Button variant="primary" icon="plus-circle" onClick={this.onAddMapping}>
|
||||
Add mapping
|
||||
</Button>
|
||||
</div>
|
||||
</PanelOptionsGroup>
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
.mapping-row {
|
||||
display: flex;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.add-mapping-row {
|
||||
display: flex;
|
||||
overflow: hidden;
|
||||
height: 37px;
|
||||
cursor: pointer;
|
||||
border-radius: $border-radius;
|
||||
width: 200px;
|
||||
}
|
||||
|
||||
.add-mapping-row-icon {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 36px;
|
||||
background-color: $green-base;
|
||||
}
|
||||
|
||||
.add-mapping-row-label {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
padding: 5px 8px;
|
||||
background-color: $input-label-bg;
|
||||
width: calc(100% - 36px);
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`Render should render component 1`] = `
|
||||
<Component
|
||||
title="Value mappings"
|
||||
>
|
||||
<div>
|
||||
<LegacyMappingRow
|
||||
key="Ok-0"
|
||||
removeValueMapping={[Function]}
|
||||
updateValueMapping={[Function]}
|
||||
valueMapping={
|
||||
Object {
|
||||
"id": 1,
|
||||
"operator": "",
|
||||
"text": "Ok",
|
||||
"type": 1,
|
||||
"value": "20",
|
||||
}
|
||||
}
|
||||
/>
|
||||
<LegacyMappingRow
|
||||
key="Meh-1"
|
||||
removeValueMapping={[Function]}
|
||||
updateValueMapping={[Function]}
|
||||
valueMapping={
|
||||
Object {
|
||||
"from": "21",
|
||||
"id": 2,
|
||||
"operator": "",
|
||||
"text": "Meh",
|
||||
"to": "30",
|
||||
"type": 2,
|
||||
}
|
||||
}
|
||||
/>
|
||||
<Button
|
||||
icon="plus-circle"
|
||||
onClick={[Function]}
|
||||
variant="primary"
|
||||
>
|
||||
Add mapping
|
||||
</Button>
|
||||
</div>
|
||||
</Component>
|
||||
`;
|
||||
@@ -10,9 +10,7 @@
|
||||
@import 'RefreshPicker/RefreshPicker';
|
||||
@import 'Forms/Legacy/Select/Select';
|
||||
@import 'TableInputCSV/TableInputCSV';
|
||||
@import 'ThresholdsEditor/ThresholdsEditor';
|
||||
@import 'TimePicker/TimeOfDayPicker';
|
||||
@import 'Tooltip/Tooltip';
|
||||
@import 'ValueMappingsEditor/ValueMappingsEditor';
|
||||
@import 'Alert/Alert';
|
||||
@import 'Slider/Slider';
|
||||
|
||||
@@ -21,7 +21,6 @@ export { ColorPicker, SeriesColorPicker } from './ColorPicker/ColorPicker';
|
||||
export { SeriesColorPickerPopover, SeriesColorPickerPopoverWithTheme } from './ColorPicker/SeriesColorPickerPopover';
|
||||
export { PanelOptionsGroup } from './PanelOptionsGroup/PanelOptionsGroup';
|
||||
export { PanelOptionsGrid } from './PanelOptionsGrid/PanelOptionsGrid';
|
||||
export { LegacyValueMappingsEditor } from './ValueMappingsEditor/LegacyValueMappingsEditor';
|
||||
export { EmptySearchResult } from './EmptySearchResult/EmptySearchResult';
|
||||
export { PieChart, PieChartType } from './PieChart/PieChart';
|
||||
export { UnitPicker } from './UnitPicker/UnitPicker';
|
||||
@@ -92,7 +91,6 @@ export { getLogRowStyles } from './Logs/getLogRowStyles';
|
||||
export { ToggleButtonGroup, ToggleButton } from './ToggleButtonGroup/ToggleButtonGroup';
|
||||
// Panel editors
|
||||
export { FullWidthButtonContainer } from './Button/FullWidthButtonContainer';
|
||||
export { ThresholdsEditor } from './ThresholdsEditor/ThresholdsEditor';
|
||||
export { ClickOutsideWrapper } from './ClickOutsideWrapper/ClickOutsideWrapper';
|
||||
export * from './SingleStatShared/index';
|
||||
export { CallToActionCard } from './CallToActionCard/CallToActionCard';
|
||||
|
||||
Reference in New Issue
Block a user