Commit 013d46b7 by Hugo Häggmark

Refactored ValueMappings

parent c90979a8
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>
......
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: [
{ id: 1, operator: '', type: MappingType.ValueToText, value: '20', text: 'Ok' },
{ id: 2, operator: '', type: MappingType.RangeToText, from: '21', to: '30', text: 'Meh' },
],
},
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);
});
......
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
......
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;
}
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;
}
......
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 = () =>
......
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> {}
......
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} />
</>
);
}
......
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' },
......
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,
......
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);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment