Merge pull request #14909 from grafana/hugoh/move-valuemappings-to-ui-components

Move ValueMappings to UI Components
This commit is contained in:
Torkel Ödegaard 2019-01-16 14:10:25 +01:00 committed by GitHub
commit ff8592d1fe
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
22 changed files with 127 additions and 123 deletions

View File

@ -1,5 +1,5 @@
import React, { SFC, ReactNode } from 'react';
import { Tooltip } from '@grafana/ui';
import { Tooltip } from '../Tooltip/Tooltip';
interface Props {
tooltip?: string;

View File

@ -1,22 +1,23 @@
import React, { PureComponent } from 'react';
import { MappingType, RangeMap, Select, ValueMap } from '@grafana/ui';
import { Label } from 'app/core/components/Label/Label';
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 = [
@ -25,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() {
@ -136,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>

View File

@ -1,27 +1,23 @@
import React from 'react';
import { shallow } from 'enzyme';
import { GaugeOptions, MappingType, PanelOptionsProps } from '@grafana/ui';
import { defaultProps } from 'app/plugins/panel/gauge/GaugePanelOptions';
import ValueMappings from './ValueMappings';
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);
const wrapper = shallow(<ValueMappings {...props} />);
const wrapper = shallow(<ValueMappingsEditor {...props} />);
const instance = wrapper.instance() as ValueMappings;
const instance = wrapper.instance() as ValueMappingsEditor;
return {
instance,
@ -40,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' },
]);
});
@ -67,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);
});

View File

@ -1,33 +1,39 @@
import React, { PureComponent } from 'react';
import { GaugeOptions, PanelOptionsProps, MappingType, RangeMap, ValueMap, PanelOptionsGroup } from '@grafana/ui';
import MappingRow from './MappingRow';
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 default class ValueMappings 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: '',
@ -41,23 +47,23 @@ export default class ValueMappings extends PureComponent<PanelOptionsProps<Gauge
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 };
}
@ -66,24 +72,24 @@ export default class ValueMappings extends PureComponent<PanelOptionsProps<Gauge
}),
}),
() => {
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>

View File

@ -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

View File

@ -6,3 +6,4 @@
@import 'PanelOptionsGroup/PanelOptionsGroup';
@import 'PanelOptionsGrid/PanelOptionsGrid';
@import 'ColorPicker/ColorPicker';
@import 'ValueMappingsEditor/ValueMappingsEditor';

View File

@ -2,6 +2,7 @@ export { DeleteButton } from './DeleteButton/DeleteButton';
export { Tooltip } from './Tooltip/Tooltip';
export { Portal } from './Portal/Portal';
export { CustomScrollbar } from './CustomScrollbar/CustomScrollbar';
export { Label } from './Label/Label';
// Select
export { Select, AsyncSelect, SelectOptionItem } from './Select/Select';
@ -18,3 +19,4 @@ export { GfFormLabel } from './GfFormLabel/GfFormLabel';
export { Graph } from './Graph/Graph';
export { PanelOptionsGroup } from './PanelOptionsGroup/PanelOptionsGroup';
export { PanelOptionsGrid } from './PanelOptionsGrid/PanelOptionsGrid';
export { ValueMappingsEditor } from './ValueMappingsEditor/ValueMappingsEditor';

View File

@ -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;
}

View File

@ -1,4 +1,3 @@
export * from './series';
export * from './time';
export * from './panel';
export * from './gauge';

View File

@ -56,6 +56,8 @@ interface BaseMap {
type: MappingType;
}
export type ValueMapping = ValueMap | RangeMap;
export interface ValueMap extends BaseMap {
value: string;
}

View File

@ -1,7 +1,6 @@
import React, { PureComponent } from 'react';
import { Select, Label } from '@grafana/ui';
import { Label } from 'app/core/components/Label/Label';
import { Select } from '@grafana/ui';
import { getBackendSrv, BackendSrv } from 'app/core/services/backend_srv';
import { DashboardSearchHit } from 'app/types';

View File

@ -1,5 +1,6 @@
import React, { SFC } from 'react';
import { Label } from 'app/core/components/Label/Label';
import { Label } from '@grafana/ui';
import { Switch } from '../../../core/components/Switch/Switch';
export interface Props {

View File

@ -1,7 +1,7 @@
import React from 'react';
import { connect } from 'react-redux';
import { Label } from '@grafana/ui';
import { Label } from 'app/core/components/Label/Label';
import { SharedPreferences } from 'app/core/components/SharedPreferences/SharedPreferences';
import { updateTeam } from './state/actions';
import { getRouteParamsId } from 'app/core/selectors/location';

View File

@ -1,8 +1,8 @@
import React, { PureComponent } from 'react';
import { GaugeOptions, PanelOptionsProps, PanelOptionsGroup } from '@grafana/ui';
import { PanelOptionsProps, PanelOptionsGroup, Label } from '@grafana/ui';
import { Switch } from 'app/core/components/Switch/Switch';
import { Label } from '../../../core/components/Label/Label';
import { GaugeOptions } from './types';
export default class GaugeOptionsEditor extends PureComponent<PanelOptionsProps<GaugeOptions>> {
onToggleThresholdLabels = () =>

View File

@ -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> {}

View File

@ -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 ValueMappings from 'app/plugins/panel/gauge/ValueMappings';
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>
<ValueMappings onChange={onChange} options={options} />
<ValueMappingsEditor onChange={this.onValueMappingsChanged} valueMappings={options.valueMappings} />
</>
);
}

View File

@ -1,9 +1,8 @@
import React, { PureComponent } from 'react';
import { GaugeOptions, PanelOptionsProps, PanelOptionsGroup } from '@grafana/ui';
import { PanelOptionsProps, PanelOptionsGroup, Label, Select } from '@grafana/ui';
import { Label } from 'app/core/components/Label/Label';
import { Select} from '@grafana/ui';
import UnitPicker from 'app/core/components/Select/UnitPicker';
import { GaugeOptions } from './types';
const statOptions = [
{ value: 'min', label: 'Min' },

View File

@ -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;
}

View File

@ -12,7 +12,7 @@ const setup = (propOverrides?: object) => {
const props: Props = {
baseColor: BasicGaugeColor.Green,
maxValue: 100,
mappings: [],
valueMappings: [],
minValue: 0,
prefix: '',
showThresholdMarkers: true,

View File

@ -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);

View File

@ -1,4 +1,4 @@
// DEPENDENCIES
// DEPENDENCIES
@import '../../node_modules/react-table/react-table.css';
// VENDOR
@ -97,7 +97,6 @@
@import 'components/add_data_source.scss';
@import 'components/page_loader';
@import 'components/toggle_button_group';
@import 'components/value-mappings';
@import 'components/popover-box';
// LOAD @grafana/ui components