diff --git a/pkg/api/frontendsettings.go b/pkg/api/frontendsettings.go index 65affe83e98..8f5457cf271 100644 --- a/pkg/api/frontendsettings.go +++ b/pkg/api/frontendsettings.go @@ -186,16 +186,18 @@ func getPanelSort(id string) int { sort = 1 case "singlestat": sort = 2 - case "table": + case "gauge": sort = 3 - case "text": + case "table": sort = 4 - case "heatmap": + case "text": sort = 5 - case "alertlist": + case "heatmap": sort = 6 - case "dashlist": + case "alertlist": sort = 7 + case "dashlist": + sort = 8 } return sort } diff --git a/public/app/core/components/Select/UnitPicker.tsx b/public/app/core/components/Select/UnitPicker.tsx index da9d4526d2b..75885cbbb84 100644 --- a/public/app/core/components/Select/UnitPicker.tsx +++ b/public/app/core/components/Select/UnitPicker.tsx @@ -3,7 +3,7 @@ import Select from './Select'; import kbn from 'app/core/utils/kbn'; interface Props { - onChange: (item: any) => {} | void; + onChange: (item: any) => void; defaultValue?: string; width?: number; } diff --git a/public/app/plugins/panel/gauge/GaugeOptions.tsx b/public/app/plugins/panel/gauge/GaugeOptions.tsx index ec3330ad62e..655e9b0a65d 100644 --- a/public/app/plugins/panel/gauge/GaugeOptions.tsx +++ b/public/app/plugins/panel/gauge/GaugeOptions.tsx @@ -1,36 +1,45 @@ import React, { PureComponent } from 'react'; import { Switch } from 'app/core/components/Switch/Switch'; import { OptionModuleProps } from './module'; +import { Label } from '../../../core/components/Label/Label'; export default class GaugeOptions extends PureComponent { - toggleThresholdLabels = () => + onToggleThresholdLabels = () => this.props.onChange({ ...this.props.options, showThresholdLabels: !this.props.options.showThresholdLabels }); - toggleThresholdMarkers = () => + onToggleThresholdMarkers = () => this.props.onChange({ ...this.props.options, showThresholdMarkers: !this.props.options.showThresholdMarkers }); + onMinValueChange = ({ target }) => this.props.onChange({ ...this.props.options, minValue: target.value }); + + onMaxValueChange = ({ target }) => this.props.onChange({ ...this.props.options, maxValue: target.value }); + render() { - const { showThresholdLabels, showThresholdMarkers } = this.props.options; + const { maxValue, minValue, showThresholdLabels, showThresholdMarkers } = this.props.options; return (
-
Gauge
-
- +
Gauge
+
+ +
-
- +
+ +
+ +
); } diff --git a/public/app/plugins/panel/gauge/MappingRow.tsx b/public/app/plugins/panel/gauge/MappingRow.tsx index 1fdb3abf9d9..35d0b2e638c 100644 --- a/public/app/plugins/panel/gauge/MappingRow.tsx +++ b/public/app/plugins/panel/gauge/MappingRow.tsx @@ -62,69 +62,59 @@ export default class MappingRow extends PureComponent { if (type === MappingType.RangeToText) { return ( -
-
+ <> +
-
- -
+
-
+
-
- -
+
-
+
-
- -
+
-
+ ); } return ( -
-
+ <> +
-
- -
+
-
+
-
- -
+
-
+ ); } @@ -132,8 +122,8 @@ export default class MappingRow extends PureComponent { const { type } = this.state; return ( -
-
+
+
this.onChangeThresholdValue(event, min)} - value={min.value} - /> -
{min.label}
-
-
, -
-
-
this.onAddThreshold(1)} className="threshold-row-add"> - -
-
Add new threshold by clicking the line.
-
-
, -
-
-
- this.onChangeThresholdValue(event, max)} - value={max.value} - /> -
{max.label}
-
-
, - ]; - } - renderThresholds() { const { thresholds } = this.state; return thresholds.map((threshold, index) => { - const rowStyle = classNames({ - 'threshold-row': true, - 'threshold-row-min': index === 0, - 'threshold-row-max': index === thresholds.length - 1, - }); - return ( -
+
{threshold.color && ( @@ -190,103 +145,80 @@ export default class Thresholds extends PureComponent value={threshold.value} onBlur={this.onBlur} /> - {threshold.canRemove ? ( -
this.onRemoveThreshold(threshold)} className="threshold-row-remove"> - -
- ) : ( -
{threshold.label}
- )} +
this.onRemoveThreshold(threshold)} className="threshold-row-remove"> + +
); }); } - insertAtIndex(index) { - const { thresholds } = this.state; - - // If thresholds.length is greater or equal to 3 - // it means a user has added one threshold - if (thresholds.length < 3 || index < 0) { - return 1; - } - - return index; - } - - renderIndicatorSection(index) { - const { thresholds } = this.state; - const indicators = thresholds.length - 1; - - if (index === 0 || index === thresholds.length) { - return ( -
-
this.onAddThreshold(this.insertAtIndex(index - 1))} - style={{ - height: '100%', - background: this.getIndicatorColor(index), - }} - /> -
- ); - } - - return ( -
-
this.onAddThreshold(this.insertAtIndex(index))} - style={{ - height: '50%', - background: this.getIndicatorColor(index), - }} - /> -
this.onAddThreshold(this.insertAtIndex(index + 1))} - style={{ - height: `50%`, - background: this.getIndicatorColor(index), - }} - /> -
- ); - } - renderIndicator() { const { thresholds } = this.state; return thresholds.map((t, i) => { - if (i <= thresholds.length - 1) { - return this.renderIndicatorSection(i); - } - - return null; + return ( +
+
this.onAddThreshold(t.index + 1)} + style={{ + height: '50%', + backgroundColor: t.color, + }} + /> +
this.onAddThreshold(t.index)} + style={{ + height: '50%', + backgroundColor: t.color, + }} + /> +
+ ); }); } - render() { - const { thresholds } = this.state; + renderBaseIndicator() { + return ( +
+
this.onAddThreshold(0)} + style={{ height: '100%', backgroundColor: this.props.options.baseColor }} + /> +
+ ); + } + + renderBase() { + const { baseColor } = this.props.options; + return ( +
+
+
+
+ this.onChangeBaseColor(color)} /> +
+
+
Base
+
+
+ ); + } + + render() { return (
-
Thresholds
+
Thresholds
-
{this.renderIndicator()}
+
+ {this.renderIndicator()} + {this.renderBaseIndicator()} +
- {thresholds.length > 2 ? this.renderThresholds() : this.renderNoThresholds()} + {this.renderThresholds()} + {this.renderBase()}
diff --git a/public/app/plugins/panel/gauge/ValueMappings.tsx b/public/app/plugins/panel/gauge/ValueMappings.tsx index c66ab960872..2197002a135 100644 --- a/public/app/plugins/panel/gauge/ValueMappings.tsx +++ b/public/app/plugins/panel/gauge/ValueMappings.tsx @@ -76,7 +76,7 @@ export default class ValueMappings extends PureComponent -
Value mappings
+
Value mappings
{mappings.length > 0 && mappings.map((mapping, index) => ( diff --git a/public/app/plugins/panel/gauge/ValueOptions.tsx b/public/app/plugins/panel/gauge/ValueOptions.tsx index 7f4142753b8..445d6517c5a 100644 --- a/public/app/plugins/panel/gauge/ValueOptions.tsx +++ b/public/app/plugins/panel/gauge/ValueOptions.tsx @@ -40,8 +40,8 @@ export default class ValueOptions extends PureComponent { return (
-
Value
-
+
Value
+
{ onChange={this.onDecimalChange} />
-
+
-
+
diff --git a/public/app/plugins/panel/gauge/__snapshots__/ValueMappings.test.tsx.snap b/public/app/plugins/panel/gauge/__snapshots__/ValueMappings.test.tsx.snap index 987a5f31853..8a05cb7e91b 100644 --- a/public/app/plugins/panel/gauge/__snapshots__/ValueMappings.test.tsx.snap +++ b/public/app/plugins/panel/gauge/__snapshots__/ValueMappings.test.tsx.snap @@ -5,7 +5,7 @@ exports[`Render should render component 1`] = ` className="section gf-form-group" >
Value mappings
diff --git a/public/app/plugins/panel/gauge/module.tsx b/public/app/plugins/panel/gauge/module.tsx index 84579a77343..152e7c20b5e 100644 --- a/public/app/plugins/panel/gauge/module.tsx +++ b/public/app/plugins/panel/gauge/module.tsx @@ -16,15 +16,18 @@ import { } from 'app/types'; export interface OptionsProps { + baseColor: string; decimals: number; + mappings: Array; + maxValue: number; + minValue: number; prefix: string; showThresholdLabels: boolean; showThresholdMarkers: boolean; stat: string; suffix: string; - unit: string; thresholds: Threshold[]; - mappings: Array; + unit: string; } export interface OptionModuleProps { @@ -34,6 +37,7 @@ export interface OptionModuleProps { export const defaultProps = { options: { + baseColor: BasicGaugeColor.Green, minValue: 0, maxValue: 100, prefix: '', @@ -44,10 +48,7 @@ export const defaultProps = { stat: '', unit: '', mappings: [], - thresholds: [ - { index: 0, label: 'Min', value: 0, canRemove: false, color: BasicGaugeColor.Green }, - { index: 1, label: 'Max', value: 100, canRemove: false }, - ], + thresholds: [], }, }; diff --git a/public/app/types/panel.ts b/public/app/types/panel.ts index 92c5125d3d6..af371e16573 100644 --- a/public/app/types/panel.ts +++ b/public/app/types/panel.ts @@ -31,10 +31,8 @@ export interface PanelMenuItem { export interface Threshold { index: number; - label: string; value: number; color?: string; - canRemove: boolean; } export enum MappingType { @@ -43,9 +41,8 @@ export enum MappingType { } export enum BasicGaugeColor { - Green = 'rgba(50, 172, 45, 0.97)', - Orange = 'rgba(237, 129, 40, 0.89)', - Red = 'rgb(212, 74, 58)', + Green = '#299c46', + Red = '#d44a3a', } interface BaseMap { diff --git a/public/app/viz/Gauge.tsx b/public/app/viz/Gauge.tsx index d918752f287..9907ddf575f 100644 --- a/public/app/viz/Gauge.tsx +++ b/public/app/viz/Gauge.tsx @@ -1,10 +1,11 @@ import React, { PureComponent } from 'react'; import $ from 'jquery'; -import { MappingType, RangeMap, Threshold, TimeSeriesVMs, ValueMap } from 'app/types'; +import { BasicGaugeColor, MappingType, RangeMap, Threshold, TimeSeriesVMs, ValueMap } from 'app/types'; import config from '../core/config'; import kbn from '../core/utils/kbn'; interface Props { + baseColor: string; decimals: number; height: number; mappings: Array; @@ -25,6 +26,7 @@ export class Gauge extends PureComponent { canvasElement: any; static defaultProps = { + baseColor: BasicGaugeColor.Green, maxValue: 100, mappings: [], minValue: 0, @@ -32,11 +34,9 @@ export class Gauge extends PureComponent { showThresholdMarkers: true, showThresholdLabels: false, suffix: '', - thresholds: [ - { label: 'Min', value: 0, color: 'rgba(50, 172, 45, 0.97)' }, - { label: 'Max', value: 100, color: 'rgba(245, 54, 54, 0.9)' }, - ], + thresholds: [], unit: 'none', + stat: 'avg', }; componentDidMount() { @@ -92,12 +92,44 @@ export class Gauge extends PureComponent { return `${prefix} ${formattedValue} ${suffix}`; } + getFontColor(value) { + const { baseColor, maxValue, thresholds } = this.props; + + const atThreshold = thresholds.filter(threshold => value <= threshold.value); + + if (atThreshold.length > 0) { + return atThreshold[0].color; + } else if (value <= maxValue) { + return BasicGaugeColor.Red; + } + + return baseColor; + } + draw() { - const { timeSeries, showThresholdLabels, showThresholdMarkers, thresholds, width, height, stat } = this.props; + const { + baseColor, + maxValue, + minValue, + timeSeries, + showThresholdLabels, + showThresholdMarkers, + thresholds, + width, + height, + stat, + } = this.props; + + let value: string | number = ''; + + if (timeSeries[0]) { + value = timeSeries[0].stats[stat]; + } else { + value = 'N/A'; + } const dimension = Math.min(width, height * 1.3); const backgroundColor = config.bootData.user.lightTheme ? 'rgb(230,230,230)' : 'rgb(38,38,38)'; - const fontColor = config.bootData.user.lightTheme ? 'rgb(38,38,38)' : 'rgb(230,230,230)'; const fontScale = parseInt('80', 10) / 100; const fontSize = Math.min(dimension / 5, 100) * fontScale; const gaugeWidthReduceRatio = showThresholdLabels ? 1.5 : 1; @@ -105,20 +137,26 @@ export class Gauge extends PureComponent { const thresholdMarkersWidth = gaugeWidth / 5; const thresholdLabelFontSize = fontSize / 2.5; - const formattedThresholds = thresholds.map((threshold, index) => { - return { - value: threshold.value, - // Hacky way to get correct color for threshold. - color: index === 0 ? threshold.color : thresholds[index - 1].color, - }; - }); + const formattedThresholds = [ + { value: minValue, color: BasicGaugeColor.Green }, + ...thresholds.map((threshold, index) => { + return { + value: threshold.value, + color: index === 0 ? threshold.color : thresholds[index].color, + }; + }), + { + value: maxValue, + color: thresholds.length > 0 ? BasicGaugeColor.Red : baseColor, + }, + ]; const options = { series: { gauges: { gauge: { - min: thresholds[0].value, - max: thresholds[thresholds.length - 1].value, + min: minValue, + max: maxValue, background: { color: backgroundColor }, border: { color: null }, shadow: { show: false }, @@ -139,13 +177,9 @@ export class Gauge extends PureComponent { width: thresholdMarkersWidth, }, value: { - color: fontColor, + color: this.getFontColor(value), formatter: () => { - if (timeSeries[0]) { - return this.formatValue(timeSeries[0].stats[stat]); - } - - return ''; + return this.formatValue(value); }, font: { size: fontSize, @@ -157,11 +191,6 @@ export class Gauge extends PureComponent { }, }; - let value: string | number = 'N/A'; - if (timeSeries.length) { - value = timeSeries[0].stats[stat]; - } - const plotSeries = { data: [[0, value]], }; diff --git a/public/sass/components/_gf-form.scss b/public/sass/components/_gf-form.scss index f0855ead897..e86b901bb79 100644 --- a/public/sass/components/_gf-form.scss +++ b/public/sass/components/_gf-form.scss @@ -123,6 +123,15 @@ $input-border: 1px solid $input-border-color; padding-left: 0px; } + &--btn { + border-right: $input-btn-border-width solid $input-label-border-color; + border-radius: $border-radius; + + &:hover { + background: $list-item-hover-bg; + } + } + &:disabled { color: $text-color-weak; } diff --git a/public/sass/components/_thresholds.scss b/public/sass/components/_thresholds.scss index 5e516278b3e..dbcc9951ff7 100644 --- a/public/sass/components/_thresholds.scss +++ b/public/sass/components/_thresholds.scss @@ -1,6 +1,5 @@ .thresholds { display: flex; - margin-top: 30px; } .threshold-rows { @@ -10,7 +9,7 @@ .threshold-row { display: flex; align-items: center; - margin: 5px 0; + margin-top: 3px; padding: 5px; &::before { @@ -25,8 +24,11 @@ border-radius: $border-radius; display: flex; overflow: hidden; - width: 300px; height: 37px; + + &--base { + width: auto; + } } .threshold-row-color { @@ -48,13 +50,12 @@ .threshold-row-input { padding: 8px 10px; - width: 230px; + width: 150px; } .threshold-row-label { background-color: $input-label-bg; padding: 5px; - width: 36px; display: flex; align-items: center; } @@ -65,12 +66,7 @@ padding: 5px 8px; } -.threshold-row-min { - margin-top: -22px; -} - -.threshold-row-max { - margin-bottom: -22px; +.threshold-row-base { } .threshold-row-remove { @@ -98,11 +94,13 @@ .indicator-section { width: 100%; + height: 50px; cursor: pointer; } .color-indicators { width: 15px; - border-radius: $border-radius; + border-bottom-left-radius: $border-radius; + border-bottom-right-radius: $border-radius; overflow: hidden; } diff --git a/public/sass/components/_value-mappings.scss b/public/sass/components/_value-mappings.scss index b1f53c755f4..1d62b191763 100644 --- a/public/sass/components/_value-mappings.scss +++ b/public/sass/components/_value-mappings.scss @@ -3,14 +3,6 @@ margin-bottom: 10px; } -.mapping-row-type { - margin-right: 5px; -} - -.mapping-row-input { - margin-right: 5px; -} - .add-mapping-row { display: flex; overflow: hidden;