mirror of
https://github.com/grafana/grafana.git
synced 2025-02-10 23:55:47 -06:00
Refactored ValueMappings
This commit is contained in:
parent
c90979a8f0
commit
013d46b707
@ -1,23 +1,23 @@
|
||||
import React, { PureComponent } from 'react';
|
||||
|
||||
import { ValueMap, RangeMap, MappingType } from '../../types/panel';
|
||||
import { MappingType, ValueMapping } from '../../types/panel';
|
||||
import { Label } from '../Label/Label';
|
||||
import { Select } from '../Select/Select';
|
||||
|
||||
interface Props {
|
||||
mapping: ValueMap | RangeMap;
|
||||
updateMapping: (mapping) => void;
|
||||
removeMapping: () => void;
|
||||
export interface Props {
|
||||
valueMapping: ValueMapping;
|
||||
updateValueMapping: (valueMapping: ValueMapping) => void;
|
||||
removeValueMapping: () => void;
|
||||
}
|
||||
|
||||
interface State {
|
||||
from: string;
|
||||
from?: string;
|
||||
id: number;
|
||||
operator: string;
|
||||
text: string;
|
||||
to: string;
|
||||
to?: string;
|
||||
type: MappingType;
|
||||
value: string;
|
||||
value?: string;
|
||||
}
|
||||
|
||||
const mappingOptions = [
|
||||
@ -26,36 +26,34 @@ const mappingOptions = [
|
||||
];
|
||||
|
||||
export default class MappingRow extends PureComponent<Props, State> {
|
||||
constructor(props) {
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
...props.mapping,
|
||||
};
|
||||
this.state = { ...props.valueMapping };
|
||||
}
|
||||
|
||||
onMappingValueChange = event => {
|
||||
onMappingValueChange = (event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
this.setState({ value: event.target.value });
|
||||
};
|
||||
|
||||
onMappingFromChange = event => {
|
||||
onMappingFromChange = (event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
this.setState({ from: event.target.value });
|
||||
};
|
||||
|
||||
onMappingToChange = event => {
|
||||
onMappingToChange = (event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
this.setState({ to: event.target.value });
|
||||
};
|
||||
|
||||
onMappingTextChange = event => {
|
||||
onMappingTextChange = (event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
this.setState({ text: event.target.value });
|
||||
};
|
||||
|
||||
onMappingTypeChange = mappingType => {
|
||||
onMappingTypeChange = (mappingType: MappingType) => {
|
||||
this.setState({ type: mappingType });
|
||||
};
|
||||
|
||||
updateMapping = () => {
|
||||
this.props.updateMapping({ ...this.state });
|
||||
this.props.updateValueMapping({ ...this.state } as ValueMapping);
|
||||
};
|
||||
|
||||
renderRow() {
|
||||
@ -137,7 +135,7 @@ export default class MappingRow extends PureComponent<Props, State> {
|
||||
</div>
|
||||
{this.renderRow()}
|
||||
<div className="gf-form">
|
||||
<button onClick={this.props.removeMapping} className="gf-form-label gf-form-label--btn">
|
||||
<button onClick={this.props.removeValueMapping} className="gf-form-label gf-form-label--btn">
|
||||
<i className="fa fa-times" />
|
||||
</button>
|
||||
</div>
|
||||
|
@ -1,21 +1,16 @@
|
||||
import React from 'react';
|
||||
import { shallow } from 'enzyme';
|
||||
|
||||
import { defaultProps } from 'app/plugins/panel/gauge/GaugePanelOptions';
|
||||
import { ValueMappingsEditor } from './ValueMappingsEditor';
|
||||
import { PanelOptionsProps, MappingType } from '../../types/panel';
|
||||
import { GaugeOptions } from '../../types/gauge';
|
||||
import { ValueMappingsEditor, Props } from './ValueMappingsEditor';
|
||||
import { MappingType } from '../../types/panel';
|
||||
|
||||
const setup = (propOverrides?: object) => {
|
||||
const props: PanelOptionsProps<GaugeOptions> = {
|
||||
const props: Props = {
|
||||
onChange: jest.fn(),
|
||||
options: {
|
||||
...defaultProps.options,
|
||||
mappings: [
|
||||
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);
|
||||
@ -41,18 +36,20 @@ describe('Render', () => {
|
||||
describe('On remove mapping', () => {
|
||||
it('Should remove mapping with id 0', () => {
|
||||
const { instance } = setup();
|
||||
|
||||
instance.onRemoveMapping(1);
|
||||
|
||||
expect(instance.state.mappings).toEqual([
|
||||
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.mappings).toEqual([
|
||||
expect(instance.state.valueMappings).toEqual([
|
||||
{ id: 1, operator: '', type: MappingType.ValueToText, value: '20', text: 'Ok' },
|
||||
]);
|
||||
});
|
||||
@ -68,7 +65,7 @@ describe('Next id to add', () => {
|
||||
});
|
||||
|
||||
it('should default to 1', () => {
|
||||
const { instance } = setup({ options: { ...defaultProps.options } });
|
||||
const { instance } = setup({ valueMappings: [] });
|
||||
|
||||
expect(instance.state.nextIdToAdd).toEqual(1);
|
||||
});
|
||||
|
@ -1,35 +1,39 @@
|
||||
import React, { PureComponent } from 'react';
|
||||
|
||||
import MappingRow from './MappingRow';
|
||||
import { PanelOptionsProps, ValueMap, RangeMap, MappingType } from '../../types/panel';
|
||||
import { GaugeOptions } from '../../types/gauge';
|
||||
import { MappingType, ValueMapping } from '../../types/panel';
|
||||
import { PanelOptionsGroup } from '../PanelOptionsGroup/PanelOptionsGroup';
|
||||
|
||||
export interface Props {
|
||||
valueMappings: ValueMapping[];
|
||||
onChange: (valueMappings: ValueMapping[]) => void;
|
||||
}
|
||||
|
||||
interface State {
|
||||
mappings: Array<ValueMap | RangeMap>;
|
||||
valueMappings: ValueMapping[];
|
||||
nextIdToAdd: number;
|
||||
}
|
||||
|
||||
export class ValueMappingsEditor extends PureComponent<PanelOptionsProps<GaugeOptions>, State> {
|
||||
constructor(props) {
|
||||
export class ValueMappingsEditor extends PureComponent<Props, State> {
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
|
||||
const mappings = props.options.mappings;
|
||||
const mappings = props.valueMappings;
|
||||
|
||||
this.state = {
|
||||
mappings: mappings || [],
|
||||
nextIdToAdd: mappings.length > 0 ? this.getMaxIdFromMappings(mappings) : 1,
|
||||
valueMappings: mappings,
|
||||
nextIdToAdd: mappings.length > 0 ? this.getMaxIdFromValueMappings(mappings) : 1,
|
||||
};
|
||||
}
|
||||
|
||||
getMaxIdFromMappings(mappings) {
|
||||
getMaxIdFromValueMappings(mappings: ValueMapping[]) {
|
||||
return Math.max.apply(null, mappings.map(mapping => mapping.id).map(m => m)) + 1;
|
||||
}
|
||||
|
||||
addMapping = () =>
|
||||
this.setState(prevState => ({
|
||||
mappings: [
|
||||
...prevState.mappings,
|
||||
valueMappings: [
|
||||
...prevState.valueMappings,
|
||||
{
|
||||
id: prevState.nextIdToAdd,
|
||||
operator: '',
|
||||
@ -43,23 +47,23 @@ export class ValueMappingsEditor extends PureComponent<PanelOptionsProps<GaugeOp
|
||||
nextIdToAdd: prevState.nextIdToAdd + 1,
|
||||
}));
|
||||
|
||||
onRemoveMapping = id => {
|
||||
onRemoveMapping = (id: number) => {
|
||||
this.setState(
|
||||
prevState => ({
|
||||
mappings: prevState.mappings.filter(m => {
|
||||
valueMappings: prevState.valueMappings.filter(m => {
|
||||
return m.id !== id;
|
||||
}),
|
||||
}),
|
||||
() => {
|
||||
this.props.onChange({ ...this.props.options, mappings: this.state.mappings });
|
||||
this.props.onChange(this.state.valueMappings);
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
updateGauge = mapping => {
|
||||
updateGauge = (mapping: ValueMapping) => {
|
||||
this.setState(
|
||||
prevState => ({
|
||||
mappings: prevState.mappings.map(m => {
|
||||
valueMappings: prevState.valueMappings.map(m => {
|
||||
if (m.id === mapping.id) {
|
||||
return { ...mapping };
|
||||
}
|
||||
@ -68,24 +72,24 @@ export class ValueMappingsEditor extends PureComponent<PanelOptionsProps<GaugeOp
|
||||
}),
|
||||
}),
|
||||
() => {
|
||||
this.props.onChange({ ...this.props.options, mappings: this.state.mappings });
|
||||
this.props.onChange(this.state.valueMappings);
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
render() {
|
||||
const { mappings } = this.state;
|
||||
const { valueMappings } = this.state;
|
||||
|
||||
return (
|
||||
<PanelOptionsGroup title="Value Mappings">
|
||||
<div>
|
||||
{mappings.length > 0 &&
|
||||
mappings.map((mapping, index) => (
|
||||
{valueMappings.length > 0 &&
|
||||
valueMappings.map((valueMapping, index) => (
|
||||
<MappingRow
|
||||
key={`${mapping.text}-${index}`}
|
||||
mapping={mapping}
|
||||
updateMapping={this.updateGauge}
|
||||
removeMapping={() => this.onRemoveMapping(mapping.id)}
|
||||
key={`${valueMapping.text}-${index}`}
|
||||
valueMapping={valueMapping}
|
||||
updateValueMapping={this.updateGauge}
|
||||
removeValueMapping={() => this.onRemoveMapping(valueMapping.id)}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
|
@ -7,7 +7,9 @@ exports[`Render should render component 1`] = `
|
||||
<div>
|
||||
<MappingRow
|
||||
key="Ok-0"
|
||||
mapping={
|
||||
removeValueMapping={[Function]}
|
||||
updateValueMapping={[Function]}
|
||||
valueMapping={
|
||||
Object {
|
||||
"id": 1,
|
||||
"operator": "",
|
||||
@ -16,12 +18,12 @@ exports[`Render should render component 1`] = `
|
||||
"value": "20",
|
||||
}
|
||||
}
|
||||
removeMapping={[Function]}
|
||||
updateMapping={[Function]}
|
||||
/>
|
||||
<MappingRow
|
||||
key="Meh-1"
|
||||
mapping={
|
||||
removeValueMapping={[Function]}
|
||||
updateValueMapping={[Function]}
|
||||
valueMapping={
|
||||
Object {
|
||||
"from": "21",
|
||||
"id": 2,
|
||||
@ -31,8 +33,6 @@ exports[`Render should render component 1`] = `
|
||||
"type": 2,
|
||||
}
|
||||
}
|
||||
removeMapping={[Function]}
|
||||
updateMapping={[Function]}
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
|
@ -1,16 +0,0 @@
|
||||
import { RangeMap, Threshold, ValueMap } from './panel';
|
||||
|
||||
export interface GaugeOptions {
|
||||
baseColor: string;
|
||||
decimals: number;
|
||||
mappings: Array<RangeMap | ValueMap>;
|
||||
maxValue: number;
|
||||
minValue: number;
|
||||
prefix: string;
|
||||
showThresholdLabels: boolean;
|
||||
showThresholdMarkers: boolean;
|
||||
stat: string;
|
||||
suffix: string;
|
||||
thresholds: Threshold[];
|
||||
unit: string;
|
||||
}
|
@ -1,4 +1,3 @@
|
||||
export * from './series';
|
||||
export * from './time';
|
||||
export * from './panel';
|
||||
export * from './gauge';
|
||||
|
@ -56,6 +56,8 @@ interface BaseMap {
|
||||
type: MappingType;
|
||||
}
|
||||
|
||||
export type ValueMapping = ValueMap | RangeMap;
|
||||
|
||||
export interface ValueMap extends BaseMap {
|
||||
value: string;
|
||||
}
|
||||
|
@ -1,7 +1,8 @@
|
||||
import React, { PureComponent } from 'react';
|
||||
import { GaugeOptions, PanelOptionsProps, PanelOptionsGroup, Label } from '@grafana/ui';
|
||||
import { PanelOptionsProps, PanelOptionsGroup, Label } from '@grafana/ui';
|
||||
|
||||
import { Switch } from 'app/core/components/Switch/Switch';
|
||||
import { GaugeOptions } from './types';
|
||||
|
||||
export default class GaugeOptionsEditor extends PureComponent<PanelOptionsProps<GaugeOptions>> {
|
||||
onToggleThresholdLabels = () =>
|
||||
|
@ -1,8 +1,9 @@
|
||||
import React, { PureComponent } from 'react';
|
||||
import { GaugeOptions, PanelProps, NullValueMode } from '@grafana/ui';
|
||||
import { PanelProps, NullValueMode } from '@grafana/ui';
|
||||
|
||||
import { getTimeSeriesVMs } from 'app/viz/state/timeSeries';
|
||||
import Gauge from 'app/viz/Gauge';
|
||||
import { GaugeOptions } from './types';
|
||||
|
||||
interface Props extends PanelProps<GaugeOptions> {}
|
||||
|
||||
|
@ -1,16 +1,17 @@
|
||||
import React, { PureComponent } from 'react';
|
||||
import {
|
||||
BasicGaugeColor,
|
||||
GaugeOptions,
|
||||
PanelOptionsProps,
|
||||
ThresholdsEditor,
|
||||
Threshold,
|
||||
PanelOptionsGrid,
|
||||
ValueMappingsEditor,
|
||||
ValueMapping,
|
||||
} from '@grafana/ui';
|
||||
|
||||
import ValueOptions from 'app/plugins/panel/gauge/ValueOptions';
|
||||
import GaugeOptionsEditor from './GaugeOptionsEditor';
|
||||
import { GaugeOptions } from './types';
|
||||
|
||||
export const defaultProps = {
|
||||
options: {
|
||||
@ -24,7 +25,7 @@ export const defaultProps = {
|
||||
decimals: 0,
|
||||
stat: 'avg',
|
||||
unit: 'none',
|
||||
mappings: [],
|
||||
valueMappings: [],
|
||||
thresholds: [],
|
||||
},
|
||||
};
|
||||
@ -32,7 +33,17 @@ export const defaultProps = {
|
||||
export default class GaugePanelOptions extends PureComponent<PanelOptionsProps<GaugeOptions>> {
|
||||
static defaultProps = defaultProps;
|
||||
|
||||
onThresholdsChanged = (thresholds: Threshold[]) => this.props.onChange({ ...this.props.options, thresholds });
|
||||
onThresholdsChanged = (thresholds: Threshold[]) =>
|
||||
this.props.onChange({
|
||||
...this.props.options,
|
||||
thresholds,
|
||||
});
|
||||
|
||||
onValueMappingsChanged = (valueMappings: ValueMapping[]) =>
|
||||
this.props.onChange({
|
||||
...this.props.options,
|
||||
valueMappings,
|
||||
});
|
||||
|
||||
render() {
|
||||
const { onChange, options } = this.props;
|
||||
@ -44,7 +55,7 @@ export default class GaugePanelOptions extends PureComponent<PanelOptionsProps<G
|
||||
<ThresholdsEditor onChange={this.onThresholdsChanged} thresholds={options.thresholds} />
|
||||
</PanelOptionsGrid>
|
||||
|
||||
<ValueMappingsEditor onChange={onChange} options={options} />
|
||||
<ValueMappingsEditor onChange={this.onValueMappingsChanged} valueMappings={options.valueMappings} />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
@ -1,7 +1,8 @@
|
||||
import React, { PureComponent } from 'react';
|
||||
import { GaugeOptions, PanelOptionsProps, PanelOptionsGroup, Label, Select } from '@grafana/ui';
|
||||
import { PanelOptionsProps, PanelOptionsGroup, Label, Select } from '@grafana/ui';
|
||||
|
||||
import UnitPicker from 'app/core/components/Select/UnitPicker';
|
||||
import { GaugeOptions } from './types';
|
||||
|
||||
const statOptions = [
|
||||
{ value: 'min', label: 'Min' },
|
||||
|
@ -1,2 +1,16 @@
|
||||
import { Threshold, ValueMapping } from '@grafana/ui';
|
||||
|
||||
|
||||
export interface GaugeOptions {
|
||||
baseColor: string;
|
||||
decimals: number;
|
||||
valueMappings: ValueMapping[];
|
||||
maxValue: number;
|
||||
minValue: number;
|
||||
prefix: string;
|
||||
showThresholdLabels: boolean;
|
||||
showThresholdMarkers: boolean;
|
||||
stat: string;
|
||||
suffix: string;
|
||||
thresholds: Threshold[];
|
||||
unit: string;
|
||||
}
|
||||
|
@ -12,7 +12,7 @@ const setup = (propOverrides?: object) => {
|
||||
const props: Props = {
|
||||
baseColor: BasicGaugeColor.Green,
|
||||
maxValue: 100,
|
||||
mappings: [],
|
||||
valueMappings: [],
|
||||
minValue: 0,
|
||||
prefix: '',
|
||||
showThresholdMarkers: true,
|
||||
|
@ -1,6 +1,6 @@
|
||||
import React, { PureComponent } from 'react';
|
||||
import $ from 'jquery';
|
||||
import { BasicGaugeColor, Threshold, TimeSeriesVMs, RangeMap, ValueMap, MappingType } from '@grafana/ui';
|
||||
import { BasicGaugeColor, Threshold, TimeSeriesVMs, MappingType, ValueMapping } from '@grafana/ui';
|
||||
|
||||
import config from '../core/config';
|
||||
import kbn from '../core/utils/kbn';
|
||||
@ -9,7 +9,7 @@ export interface Props {
|
||||
baseColor: string;
|
||||
decimals: number;
|
||||
height: number;
|
||||
mappings: Array<RangeMap | ValueMap>;
|
||||
valueMappings: ValueMapping[];
|
||||
maxValue: number;
|
||||
minValue: number;
|
||||
prefix: string;
|
||||
@ -29,7 +29,7 @@ export class Gauge extends PureComponent<Props> {
|
||||
static defaultProps = {
|
||||
baseColor: BasicGaugeColor.Green,
|
||||
maxValue: 100,
|
||||
mappings: [],
|
||||
valueMappings: [],
|
||||
minValue: 0,
|
||||
prefix: '',
|
||||
showThresholdMarkers: true,
|
||||
@ -64,20 +64,17 @@ export class Gauge extends PureComponent<Props> {
|
||||
}
|
||||
})[0];
|
||||
|
||||
return {
|
||||
rangeMap,
|
||||
valueMap,
|
||||
};
|
||||
return { rangeMap, valueMap };
|
||||
}
|
||||
|
||||
formatValue(value) {
|
||||
const { decimals, mappings, prefix, suffix, unit } = this.props;
|
||||
const { decimals, valueMappings, prefix, suffix, unit } = this.props;
|
||||
|
||||
const formatFunc = kbn.valueFormats[unit];
|
||||
const formattedValue = formatFunc(value, decimals);
|
||||
|
||||
if (mappings.length > 0) {
|
||||
const { rangeMap, valueMap } = this.formatWithMappings(mappings, formattedValue);
|
||||
if (valueMappings.length > 0) {
|
||||
const { rangeMap, valueMap } = this.formatWithMappings(valueMappings, formattedValue);
|
||||
|
||||
if (valueMap) {
|
||||
return `${prefix} ${valueMap} ${suffix}`;
|
||||
@ -148,10 +145,7 @@ export class Gauge extends PureComponent<Props> {
|
||||
color: index === 0 ? threshold.color : thresholds[index].color,
|
||||
};
|
||||
}),
|
||||
{
|
||||
value: maxValue,
|
||||
color: thresholds.length > 0 ? BasicGaugeColor.Red : baseColor,
|
||||
},
|
||||
{ value: maxValue, color: thresholds.length > 0 ? BasicGaugeColor.Red : baseColor },
|
||||
];
|
||||
|
||||
const options = {
|
||||
@ -184,19 +178,14 @@ export class Gauge extends PureComponent<Props> {
|
||||
formatter: () => {
|
||||
return this.formatValue(value);
|
||||
},
|
||||
font: {
|
||||
size: fontSize,
|
||||
family: '"Helvetica Neue", Helvetica, Arial, sans-serif',
|
||||
},
|
||||
font: { size: fontSize, family: '"Helvetica Neue", Helvetica, Arial, sans-serif' },
|
||||
},
|
||||
show: true,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const plotSeries = {
|
||||
data: [[0, value]],
|
||||
};
|
||||
const plotSeries = { data: [[0, value]] };
|
||||
|
||||
try {
|
||||
$.plot(this.canvasElement, [plotSeries], options);
|
||||
|
Loading…
Reference in New Issue
Block a user