Commit f5ae40cf by Hugo Häggmark Committed by GitHub

Merge pull request #14811 from grafana/move-threshold-component-to-ui-components

Moved Thresholds and styles to grafana/ui/components
parents bc78c8d5 d97cd450
import React from 'react'; import React from 'react';
import { shallow } from 'enzyme'; import { shallow } from 'enzyme';
import Thresholds from './Thresholds';
import { defaultProps } from './GaugePanelOptions'; import { ThresholdsEditor, Props } from './ThresholdsEditor';
import { BasicGaugeColor } from 'app/types'; import { BasicGaugeColor } from '../../types';
import { PanelOptionsProps } from '@grafana/ui';
import { Options } from './types';
const setup = (propOverrides?: object) => { const setup = (propOverrides?: object) => {
const props: PanelOptionsProps<Options> = { const props: Props = {
onChange: jest.fn(), onChange: jest.fn(),
options: { thresholds: [],
...defaultProps.options,
thresholds: [],
},
}; };
Object.assign(props, propOverrides); Object.assign(props, propOverrides);
return shallow(<Thresholds {...props} />).instance() as Thresholds; return shallow(<ThresholdsEditor {...props} />).instance() as ThresholdsEditor;
}; };
describe('Add threshold', () => { describe('Add threshold', () => {
...@@ -31,10 +26,7 @@ describe('Add threshold', () => { ...@@ -31,10 +26,7 @@ describe('Add threshold', () => {
it('should add another threshold above a first', () => { it('should add another threshold above a first', () => {
const instance = setup({ const instance = setup({
options: { thresholds: [{ index: 0, value: 50, color: 'rgb(127, 115, 64)' }],
...defaultProps.options,
thresholds: [{ index: 0, value: 50, color: 'rgb(127, 115, 64)' }],
},
}); });
instance.onAddThreshold(1); instance.onAddThreshold(1);
......
import React, { PureComponent } from 'react'; import React, { PureComponent } from 'react';
import tinycolor from 'tinycolor2'; import tinycolor, { ColorInput } from 'tinycolor2';
import { ColorPicker } from '@grafana/ui';
import { BasicGaugeColor, Threshold } from 'app/types'; import { Threshold, BasicGaugeColor } from '../../types';
import { PanelOptionsProps } from '@grafana/ui'; import { ColorPicker } from '../ColorPicker/ColorPicker';
import { Options } from './types';
export interface Props {
thresholds: Threshold[];
onChange: (thresholds: Threshold[]) => void;
}
interface State { interface State {
thresholds: Threshold[]; thresholds: Threshold[];
baseColor: string; baseColor: string;
} }
export default class Thresholds extends PureComponent<PanelOptionsProps<Options>, State> { export class ThresholdsEditor extends PureComponent<Props, State> {
constructor(props) { constructor(props: Props) {
super(props); super(props);
this.state = { this.state = { thresholds: props.thresholds, baseColor: BasicGaugeColor.Green };
thresholds: props.options.thresholds,
baseColor: props.options.baseColor,
};
} }
onAddThreshold = index => { onAddThreshold = (index: number) => {
const { maxValue, minValue } = this.props.options; const maxValue = 100; // hardcoded for now before we add the base threshold
const minValue = 0; // hardcoded for now before we add the base threshold
const { thresholds } = this.state; const { thresholds } = this.state;
const newThresholds = thresholds.map(threshold => { const newThresholds = thresholds.map(threshold => {
if (threshold.index >= index) { if (threshold.index >= index) {
threshold = { ...threshold, index: threshold.index + 1 }; threshold = {
...threshold,
index: threshold.index + 1,
};
} }
return threshold; return threshold;
...@@ -48,27 +53,32 @@ export default class Thresholds extends PureComponent<PanelOptionsProps<Options> ...@@ -48,27 +53,32 @@ export default class Thresholds extends PureComponent<PanelOptionsProps<Options>
if (index === 0 && thresholds.length === 0) { if (index === 0 && thresholds.length === 0) {
color = tinycolor.mix(BasicGaugeColor.Green, BasicGaugeColor.Red, 50).toRgbString(); color = tinycolor.mix(BasicGaugeColor.Green, BasicGaugeColor.Red, 50).toRgbString();
} else { } else {
color = tinycolor.mix(thresholds[index - 1].color, BasicGaugeColor.Red, 50).toRgbString(); color = tinycolor.mix(thresholds[index - 1].color as ColorInput, BasicGaugeColor.Red, 50).toRgbString();
} }
this.setState( this.setState(
{ {
thresholds: this.sortThresholds([...newThresholds, { index: index, value: value, color: color }]), thresholds: this.sortThresholds([
...newThresholds,
{
index,
value: value as number,
color,
},
]),
}, },
() => this.updateGauge() () => this.updateGauge()
); );
}; };
onRemoveThreshold = threshold => { onRemoveThreshold = (threshold: Threshold) => {
this.setState( this.setState(
prevState => ({ prevState => ({ thresholds: prevState.thresholds.filter(t => t !== threshold) }),
thresholds: prevState.thresholds.filter(t => t !== threshold),
}),
() => this.updateGauge() () => this.updateGauge()
); );
}; };
onChangeThresholdValue = (event, threshold) => { onChangeThresholdValue = (event: any, threshold: Threshold) => {
const { thresholds } = this.state; const { thresholds } = this.state;
const newThresholds = thresholds.map(t => { const newThresholds = thresholds.map(t => {
...@@ -79,12 +89,10 @@ export default class Thresholds extends PureComponent<PanelOptionsProps<Options> ...@@ -79,12 +89,10 @@ export default class Thresholds extends PureComponent<PanelOptionsProps<Options>
return t; return t;
}); });
this.setState({ this.setState({ thresholds: newThresholds });
thresholds: newThresholds,
});
}; };
onChangeThresholdColor = (threshold, color) => { onChangeThresholdColor = (threshold: Threshold, color: string) => {
const { thresholds } = this.state; const { thresholds } = this.state;
const newThresholds = thresholds.map(t => { const newThresholds = thresholds.map(t => {
...@@ -103,20 +111,18 @@ export default class Thresholds extends PureComponent<PanelOptionsProps<Options> ...@@ -103,20 +111,18 @@ export default class Thresholds extends PureComponent<PanelOptionsProps<Options>
); );
}; };
onChangeBaseColor = color => this.props.onChange({ ...this.props.options, baseColor: color }); onChangeBaseColor = (color: string) => this.props.onChange(this.state.thresholds);
onBlur = () => { onBlur = () => {
this.setState(prevState => ({ this.setState(prevState => ({ thresholds: this.sortThresholds(prevState.thresholds) }));
thresholds: this.sortThresholds(prevState.thresholds),
}));
this.updateGauge(); this.updateGauge();
}; };
updateGauge = () => { updateGauge = () => {
this.props.onChange({ ...this.props.options, thresholds: this.state.thresholds }); this.props.onChange(this.state.thresholds);
}; };
sortThresholds = thresholds => { sortThresholds = (thresholds: Threshold[]) => {
return thresholds.sort((t1, t2) => { return thresholds.sort((t1, t2) => {
return t2.value - t1.value; return t2.value - t1.value;
}); });
...@@ -161,20 +167,8 @@ export default class Thresholds extends PureComponent<PanelOptionsProps<Options> ...@@ -161,20 +167,8 @@ export default class Thresholds extends PureComponent<PanelOptionsProps<Options>
return thresholds.map((t, i) => { return thresholds.map((t, i) => {
return ( return (
<div key={`${t.value}-${i}`} className="indicator-section"> <div key={`${t.value}-${i}`} className="indicator-section">
<div <div onClick={() => this.onAddThreshold(t.index + 1)} style={{ height: '50%', backgroundColor: t.color }} />
onClick={() => this.onAddThreshold(t.index + 1)} <div onClick={() => this.onAddThreshold(t.index)} style={{ height: '50%', backgroundColor: t.color }} />
style={{
height: '50%',
backgroundColor: t.color,
}}
/>
<div
onClick={() => this.onAddThreshold(t.index)}
style={{
height: '50%',
backgroundColor: t.color,
}}
/>
</div> </div>
); );
}); });
...@@ -185,14 +179,14 @@ export default class Thresholds extends PureComponent<PanelOptionsProps<Options> ...@@ -185,14 +179,14 @@ export default class Thresholds extends PureComponent<PanelOptionsProps<Options>
<div className="indicator-section" style={{ height: '100%' }}> <div className="indicator-section" style={{ height: '100%' }}>
<div <div
onClick={() => this.onAddThreshold(0)} onClick={() => this.onAddThreshold(0)}
style={{ height: '100%', backgroundColor: this.props.options.baseColor }} style={{ height: '100%', backgroundColor: BasicGaugeColor.Green }}
/> />
</div> </div>
); );
} }
renderBase() { renderBase() {
const { baseColor } = this.props.options; const baseColor = BasicGaugeColor.Green;
return ( return (
<div className="threshold-row threshold-row-base"> <div className="threshold-row threshold-row-base">
......
@import 'CustomScrollbar/CustomScrollbar'; @import 'CustomScrollbar/CustomScrollbar';
@import 'DeleteButton/DeleteButton'; @import 'DeleteButton/DeleteButton';
@import 'ThresholdsEditor/ThresholdsEditor';
@import 'Tooltip/Tooltip'; @import 'Tooltip/Tooltip';
@import 'Select/Select'; @import 'Select/Select';
...@@ -13,3 +13,4 @@ export { LoadingPlaceholder } from './LoadingPlaceholder/LoadingPlaceholder'; ...@@ -13,3 +13,4 @@ export { LoadingPlaceholder } from './LoadingPlaceholder/LoadingPlaceholder';
export { ColorPicker } from './ColorPicker/ColorPicker'; export { ColorPicker } from './ColorPicker/ColorPicker';
export { SeriesColorPickerPopover } from './ColorPicker/SeriesColorPickerPopover'; export { SeriesColorPickerPopover } from './ColorPicker/SeriesColorPickerPopover';
export { SeriesColorPicker } from './ColorPicker/SeriesColorPicker'; export { SeriesColorPicker } from './ColorPicker/SeriesColorPicker';
export { ThresholdsEditor } from './ThresholdsEditor/ThresholdsEditor';
import { RangeMap, ValueMap, Threshold } from 'app/types'; import { RangeMap, Threshold, ValueMap } from './panel';
export interface Options { export interface GaugeOptions {
baseColor: string; baseColor: string;
decimals: number; decimals: number;
mappings: Array<RangeMap | ValueMap>; mappings: Array<RangeMap | ValueMap>;
......
export * from './series'; export * from './series';
export * from './time'; export * from './time';
export * from './panel'; export * from './panel';
export * from './gauge';
...@@ -29,3 +29,35 @@ export interface PanelMenuItem { ...@@ -29,3 +29,35 @@ export interface PanelMenuItem {
shortcut?: string; shortcut?: string;
subMenu?: PanelMenuItem[]; subMenu?: PanelMenuItem[];
} }
export interface Threshold {
index: number;
value: number;
color?: string;
}
export enum BasicGaugeColor {
Green = '#299c46',
Red = '#d44a3a',
}
export enum MappingType {
ValueToText = 1,
RangeToText = 2,
}
interface BaseMap {
id: number;
operator: string;
text: string;
type: MappingType;
}
export interface ValueMap extends BaseMap {
value: string;
}
export interface RangeMap extends BaseMap {
from: string;
to: string;
}
import React, { PureComponent } from 'react'; import React, { PureComponent } from 'react';
import { GaugeOptions, PanelOptionsProps } from '@grafana/ui';
import { Switch } from 'app/core/components/Switch/Switch'; import { Switch } from 'app/core/components/Switch/Switch';
import { Label } from '../../../core/components/Label/Label'; import { Label } from '../../../core/components/Label/Label';
import { PanelOptionsProps } from '@grafana/ui';
import { Options } from './types';
export default class GaugeOptions extends PureComponent<PanelOptionsProps<Options>> { export default class GaugeOptionsEditor extends PureComponent<PanelOptionsProps<GaugeOptions>> {
onToggleThresholdLabels = () => onToggleThresholdLabels = () =>
this.props.onChange({ ...this.props.options, showThresholdLabels: !this.props.options.showThresholdLabels }); this.props.onChange({ ...this.props.options, showThresholdLabels: !this.props.options.showThresholdLabels });
......
import React, { PureComponent } from 'react'; import React, { PureComponent } from 'react';
import { PanelProps, NullValueMode } from '@grafana/ui'; import { GaugeOptions, PanelProps, NullValueMode } from '@grafana/ui';
import { getTimeSeriesVMs } from 'app/viz/state/timeSeries'; import { getTimeSeriesVMs } from 'app/viz/state/timeSeries';
import Gauge from 'app/viz/Gauge'; import Gauge from 'app/viz/Gauge';
import { Options } from './types';
interface Props extends PanelProps<Options> {} interface Props extends PanelProps<GaugeOptions> {}
export class GaugePanel extends PureComponent<Props> { export class GaugePanel extends PureComponent<Props> {
render() { render() {
......
import React, { PureComponent } from 'react'; import React, { PureComponent } from 'react';
import { BasicGaugeColor, GaugeOptions, PanelOptionsProps, ThresholdsEditor, Threshold } from '@grafana/ui';
import ValueOptions from 'app/plugins/panel/gauge/ValueOptions'; import ValueOptions from 'app/plugins/panel/gauge/ValueOptions';
import Thresholds from 'app/plugins/panel/gauge/Thresholds';
import { BasicGaugeColor } from 'app/types';
import { PanelOptionsProps } from '@grafana/ui';
import ValueMappings from 'app/plugins/panel/gauge/ValueMappings'; import ValueMappings from 'app/plugins/panel/gauge/ValueMappings';
import { Options } from './types'; import GaugeOptionsEditor from './GaugeOptionsEditor';
import GaugeOptions from './GaugeOptions';
export const defaultProps = { export const defaultProps = {
options: { options: {
...@@ -24,17 +22,19 @@ export const defaultProps = { ...@@ -24,17 +22,19 @@ export const defaultProps = {
}, },
}; };
export default class GaugePanelOptions extends PureComponent<PanelOptionsProps<Options>> { export default class GaugePanelOptions extends PureComponent<PanelOptionsProps<GaugeOptions>> {
static defaultProps = defaultProps; static defaultProps = defaultProps;
onThresholdsChanged = (thresholds: Threshold[]) => this.props.onChange({ ...this.props.options, thresholds });
render() { render() {
const { onChange, options } = this.props; const { onChange, options } = this.props;
return ( return (
<> <>
<div className="form-section"> <div className="form-section">
<ValueOptions onChange={onChange} options={options} /> <ValueOptions onChange={onChange} options={options} />
<GaugeOptions onChange={onChange} options={options} /> <GaugeOptionsEditor onChange={onChange} options={options} />
<Thresholds onChange={onChange} options={options} /> <ThresholdsEditor onChange={this.onThresholdsChanged} thresholds={options.thresholds} />
</div> </div>
<div className="form-section"> <div className="form-section">
......
import React, { PureComponent } from 'react'; import React, { PureComponent } from 'react';
import { MappingType, RangeMap, Select, ValueMap } from '@grafana/ui';
import { Label } from 'app/core/components/Label/Label'; import { Label } from 'app/core/components/Label/Label';
import { Select } from '@grafana/ui';
import { MappingType, RangeMap, ValueMap } from 'app/types';
interface Props { interface Props {
mapping: ValueMap | RangeMap; mapping: ValueMap | RangeMap;
......
import React from 'react'; import React from 'react';
import { shallow } from 'enzyme'; import { shallow } from 'enzyme';
import ValueMappings from './ValueMappings'; import { GaugeOptions, MappingType, PanelOptionsProps } from '@grafana/ui';
import { MappingType } from 'app/types';
import { PanelOptionsProps } from '@grafana/ui';
import { Options } from './types';
import { defaultProps } from 'app/plugins/panel/gauge/GaugePanelOptions'; import { defaultProps } from 'app/plugins/panel/gauge/GaugePanelOptions';
import ValueMappings from './ValueMappings';
const setup = (propOverrides?: object) => { const setup = (propOverrides?: object) => {
const props: PanelOptionsProps<Options> = { const props: PanelOptionsProps<GaugeOptions> = {
onChange: jest.fn(), onChange: jest.fn(),
options: { options: {
...defaultProps.options, ...defaultProps.options,
......
import React, { PureComponent } from 'react'; import React, { PureComponent } from 'react';
import { GaugeOptions, PanelOptionsProps, MappingType, RangeMap, ValueMap } from '@grafana/ui';
import MappingRow from './MappingRow'; import MappingRow from './MappingRow';
import { MappingType, RangeMap, ValueMap } from 'app/types';
import { PanelOptionsProps } from '@grafana/ui';
import { Options } from './types';
interface State { interface State {
mappings: Array<ValueMap | RangeMap>; mappings: Array<ValueMap | RangeMap>;
nextIdToAdd: number; nextIdToAdd: number;
} }
export default class ValueMappings extends PureComponent<PanelOptionsProps<Options>, State> { export default class ValueMappings extends PureComponent<PanelOptionsProps<GaugeOptions>, State> {
constructor(props) { constructor(props) {
super(props); super(props);
......
import React, { PureComponent } from 'react'; import React, { PureComponent } from 'react';
import { GaugeOptions, PanelOptionsProps } from '@grafana/ui';
import { Label } from 'app/core/components/Label/Label'; import { Label } from 'app/core/components/Label/Label';
import { Select} from '@grafana/ui'; import { Select} from '@grafana/ui';
import UnitPicker from 'app/core/components/Select/UnitPicker'; import UnitPicker from 'app/core/components/Select/UnitPicker';
import { PanelOptionsProps } from '@grafana/ui';
import { Options } from './types';
const statOptions = [ const statOptions = [
{ value: 'min', label: 'Min' }, { value: 'min', label: 'Min' },
...@@ -21,7 +21,7 @@ const statOptions = [ ...@@ -21,7 +21,7 @@ const statOptions = [
const labelWidth = 6; const labelWidth = 6;
export default class ValueOptions extends PureComponent<PanelOptionsProps<Options>> { export default class ValueOptions extends PureComponent<PanelOptionsProps<GaugeOptions>> {
onUnitChange = unit => this.props.onChange({ ...this.props.options, unit: unit.value }); onUnitChange = unit => this.props.onChange({ ...this.props.options, unit: unit.value });
onStatChange = stat => this.props.onChange({ ...this.props.options, stat: stat.value }); onStatChange = stat => this.props.onChange({ ...this.props.options, stat: stat.value });
......
...@@ -9,7 +9,6 @@ import { ApiKey, ApiKeysState, NewApiKey } from './apiKeys'; ...@@ -9,7 +9,6 @@ import { ApiKey, ApiKeysState, NewApiKey } from './apiKeys';
import { Invitee, OrgUser, User, UsersState, UserState } from './user'; import { Invitee, OrgUser, User, UsersState, UserState } from './user';
import { DataSource, DataSourceSelectItem, DataSourcesState } from './datasources'; import { DataSource, DataSourceSelectItem, DataSourcesState } from './datasources';
import { DataQuery, DataQueryResponse, DataQueryOptions } from './series'; import { DataQuery, DataQueryResponse, DataQueryOptions } from './series';
import { BasicGaugeColor, MappingType, RangeMap, Threshold, ValueMap } from './panel';
import { PluginDashboard, PluginMeta, Plugin, PanelPlugin, PluginsState } from './plugins'; import { PluginDashboard, PluginMeta, Plugin, PanelPlugin, PluginsState } from './plugins';
import { Organization, OrganizationState } from './organization'; import { Organization, OrganizationState } from './organization';
import { import {
...@@ -69,13 +68,8 @@ export { ...@@ -69,13 +68,8 @@ export {
AppNotificationTimeout, AppNotificationTimeout,
DashboardSearchHit, DashboardSearchHit,
UserState, UserState,
Threshold,
ValidationEvents, ValidationEvents,
ValidationRule, ValidationRule,
ValueMap,
RangeMap,
MappingType,
BasicGaugeColor,
}; };
export interface StoreState { export interface StoreState {
......
export interface Threshold {
index: number;
value: number;
color?: string;
}
export enum MappingType {
ValueToText = 1,
RangeToText = 2,
}
export enum BasicGaugeColor {
Green = '#299c46',
Red = '#d44a3a',
}
interface BaseMap {
id: number;
operator: string;
text: string;
type: MappingType;
}
export interface ValueMap extends BaseMap {
value: string;
}
export interface RangeMap extends BaseMap {
from: string;
to: string;
}
import React from 'react'; import React from 'react';
import { shallow } from 'enzyme'; import { shallow } from 'enzyme';
import { BasicGaugeColor, TimeSeriesVMs } from '@grafana/ui';
import { Gauge, Props } from './Gauge'; import { Gauge, Props } from './Gauge';
import { BasicGaugeColor } from '../types';
import { TimeSeriesVMs } from '@grafana/ui';
jest.mock('jquery', () => ({ jest.mock('jquery', () => ({
plot: jest.fn(), plot: jest.fn(),
......
import React, { PureComponent } from 'react'; import React, { PureComponent } from 'react';
import $ from 'jquery'; import $ from 'jquery';
import { BasicGaugeColor, MappingType, RangeMap, Threshold, ValueMap } from 'app/types'; import { BasicGaugeColor, Threshold, TimeSeriesVMs, RangeMap, ValueMap, MappingType } from '@grafana/ui';
import { TimeSeriesVMs } from '@grafana/ui';
import config from '../core/config'; import config from '../core/config';
import kbn from '../core/utils/kbn'; import kbn from '../core/utils/kbn';
......
...@@ -98,7 +98,6 @@ ...@@ -98,7 +98,6 @@
@import 'components/toolbar'; @import 'components/toolbar';
@import 'components/add_data_source.scss'; @import 'components/add_data_source.scss';
@import 'components/page_loader'; @import 'components/page_loader';
@import 'components/thresholds';
@import 'components/toggle_button_group'; @import 'components/toggle_button_group';
@import 'components/value-mappings'; @import 'components/value-mappings';
@import 'components/popover-box'; @import 'components/popover-box';
......
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