Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
N
nexpie-grafana-theme
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Registry
Registry
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Kornkitt Poolsup
nexpie-grafana-theme
Commits
8cd54c94
Commit
8cd54c94
authored
Mar 11, 2019
by
ryan
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
make value processing reusable
parent
6a34eb2d
Show whitespace changes
Inline
Side-by-side
Showing
9 changed files
with
342 additions
and
221 deletions
+342
-221
packages/grafana-ui/src/components/Gauge/Gauge.test.tsx
+4
-88
packages/grafana-ui/src/components/Gauge/Gauge.tsx
+10
-67
packages/grafana-ui/src/utils/valueProcessor.test.ts
+107
-0
packages/grafana-ui/src/utils/valueProcessor.ts
+97
-0
public/app/plugins/panel/gauge/DisplayValueEditor.tsx
+64
-0
public/app/plugins/panel/gauge/GaugePanel.tsx
+27
-15
public/app/plugins/panel/gauge/GaugePanelEditor.tsx
+12
-1
public/app/plugins/panel/gauge/SingleStatValueEditor.tsx
+6
-43
public/app/plugins/panel/gauge/types.ts
+15
-7
No files found.
packages/grafana-ui/src/components/Gauge/Gauge.test.tsx
View file @
8cd54c94
...
...
@@ -2,7 +2,6 @@ import React from 'react';
import
{
shallow
}
from
'enzyme'
;
import
{
Gauge
,
Props
}
from
'./Gauge'
;
import
{
ValueMapping
,
MappingType
}
from
'../../types'
;
import
{
getTheme
}
from
'../../themes'
;
jest
.
mock
(
'jquery'
,
()
=>
({
...
...
@@ -12,19 +11,16 @@ jest.mock('jquery', () => ({
const
setup
=
(
propOverrides
?:
object
)
=>
{
const
props
:
Props
=
{
maxValue
:
100
,
valueMappings
:
[],
minValue
:
0
,
prefix
:
''
,
showThresholdMarkers
:
true
,
showThresholdLabels
:
false
,
suffix
:
''
,
thresholds
:
[{
index
:
0
,
value
:
-
Infinity
,
color
:
'#7EB26D'
}],
unit
:
'none'
,
stat
:
'avg'
,
height
:
300
,
width
:
300
,
value
:
25
,
decimals
:
0
,
value
:
{
text
:
'25'
,
numeric
:
25
,
},
theme
:
getTheme
(),
};
...
...
@@ -39,38 +35,6 @@ const setup = (propOverrides?: object) => {
};
};
describe
(
'Get font color'
,
()
=>
{
it
(
'should get first threshold color when only one threshold'
,
()
=>
{
const
{
instance
}
=
setup
({
thresholds
:
[{
index
:
0
,
value
:
-
Infinity
,
color
:
'#7EB26D'
}]
});
expect
(
instance
.
getFontColor
(
49
)).
toEqual
(
'#7EB26D'
);
});
it
(
'should get the threshold color if value is same as a threshold'
,
()
=>
{
const
{
instance
}
=
setup
({
thresholds
:
[
{
index
:
2
,
value
:
75
,
color
:
'#6ED0E0'
},
{
index
:
1
,
value
:
50
,
color
:
'#EAB839'
},
{
index
:
0
,
value
:
-
Infinity
,
color
:
'#7EB26D'
},
],
});
expect
(
instance
.
getFontColor
(
50
)).
toEqual
(
'#EAB839'
);
});
it
(
'should get the nearest threshold color between thresholds'
,
()
=>
{
const
{
instance
}
=
setup
({
thresholds
:
[
{
index
:
2
,
value
:
75
,
color
:
'#6ED0E0'
},
{
index
:
1
,
value
:
50
,
color
:
'#EAB839'
},
{
index
:
0
,
value
:
-
Infinity
,
color
:
'#7EB26D'
},
],
});
expect
(
instance
.
getFontColor
(
55
)).
toEqual
(
'#EAB839'
);
});
});
describe
(
'Get thresholds formatted'
,
()
=>
{
it
(
'should return first thresholds color for min and max'
,
()
=>
{
const
{
instance
}
=
setup
({
thresholds
:
[{
index
:
0
,
value
:
-
Infinity
,
color
:
'#7EB26D'
}]
});
...
...
@@ -98,51 +62,3 @@ describe('Get thresholds formatted', () => {
]);
});
});
describe
(
'Format value'
,
()
=>
{
it
(
'should return if value isNaN'
,
()
=>
{
const
valueMappings
:
ValueMapping
[]
=
[];
const
value
=
'N/A'
;
const
{
instance
}
=
setup
({
valueMappings
});
const
result
=
instance
.
formatValue
(
value
);
expect
(
result
).
toEqual
(
'N/A'
);
});
it
(
'should return formatted value if there are no value mappings'
,
()
=>
{
const
valueMappings
:
ValueMapping
[]
=
[];
const
value
=
'6'
;
const
{
instance
}
=
setup
({
valueMappings
,
decimals
:
1
});
const
result
=
instance
.
formatValue
(
value
);
expect
(
result
).
toEqual
(
'6.0'
);
});
it
(
'should return formatted value if there are no matching value mappings'
,
()
=>
{
const
valueMappings
:
ValueMapping
[]
=
[
{
id
:
0
,
operator
:
''
,
text
:
'elva'
,
type
:
MappingType
.
ValueToText
,
value
:
'11'
},
{
id
:
1
,
operator
:
''
,
text
:
'1-9'
,
type
:
MappingType
.
RangeToText
,
from
:
'1'
,
to
:
'9'
},
];
const
value
=
'10'
;
const
{
instance
}
=
setup
({
valueMappings
,
decimals
:
1
});
const
result
=
instance
.
formatValue
(
value
);
expect
(
result
).
toEqual
(
'10.0'
);
});
it
(
'should return mapped value if there are matching value mappings'
,
()
=>
{
const
valueMappings
:
ValueMapping
[]
=
[
{
id
:
0
,
operator
:
''
,
text
:
'1-20'
,
type
:
MappingType
.
RangeToText
,
from
:
'1'
,
to
:
'20'
},
{
id
:
1
,
operator
:
''
,
text
:
'elva'
,
type
:
MappingType
.
ValueToText
,
value
:
'11'
},
];
const
value
=
'11'
;
const
{
instance
}
=
setup
({
valueMappings
,
decimals
:
1
});
const
result
=
instance
.
formatValue
(
value
);
expect
(
result
).
toEqual
(
'1-20'
);
});
});
packages/grafana-ui/src/components/Gauge/Gauge.tsx
View file @
8cd54c94
import
React
,
{
PureComponent
}
from
'react'
;
import
$
from
'jquery'
;
import
{
getMappedValue
}
from
'../../utils/valueMappings'
;
import
{
getColorFromHexRgbOrName
}
from
'../../utils/namedColorsPalette'
;
import
{
Themeable
,
GrafanaThemeType
}
from
'../../types/theme'
;
import
{
ValueMapping
,
Threshold
,
BasicGaugeColor
}
from
'../../types/panel'
;
import
{
getValueFormat
}
from
'../../utils/valueFormats/valueFormats'
;
type
TimeSeriesValue
=
string
|
number
|
null
;
import
{
Threshold
,
BasicGaugeColor
}
from
'../../types/panel'
;
import
{
DisplayValue
}
from
'../../utils/valueProcessor'
;
export
interface
Props
extends
Themeable
{
decimals
?:
number
|
null
;
width
:
number
;
height
:
number
;
valueMappings
:
ValueMapping
[];
maxValue
:
number
;
minValue
:
number
;
prefix
:
string
;
thresholds
:
Threshold
[];
showThresholdMarkers
:
boolean
;
showThresholdLabels
:
boolean
;
stat
:
string
;
suffix
:
string
;
unit
:
string
;
width
:
number
;
value
:
number
;
value
:
DisplayValue
;
}
const
FONT_SCALE
=
1
;
...
...
@@ -32,15 +24,10 @@ export class Gauge extends PureComponent<Props> {
static
defaultProps
=
{
maxValue
:
100
,
valueMappings
:
[],
minValue
:
0
,
prefix
:
''
,
showThresholdMarkers
:
true
,
showThresholdLabels
:
false
,
suffix
:
''
,
thresholds
:
[],
unit
:
'none'
,
stat
:
'avg'
,
theme
:
GrafanaThemeType
.
Dark
,
};
...
...
@@ -52,49 +39,6 @@ export class Gauge extends PureComponent<Props> {
this
.
draw
();
}
formatValue
(
value
:
TimeSeriesValue
)
{
const
{
decimals
,
valueMappings
,
prefix
,
suffix
,
unit
}
=
this
.
props
;
if
(
isNaN
(
value
as
number
))
{
return
value
;
}
if
(
valueMappings
.
length
>
0
)
{
const
valueMappedValue
=
getMappedValue
(
valueMappings
,
value
);
if
(
valueMappedValue
)
{
return
`
${
prefix
&&
prefix
+
' '
}${
valueMappedValue
.
text
}${
suffix
&&
' '
+
suffix
}
`
;
}
}
const
formatFunc
=
getValueFormat
(
unit
);
const
formattedValue
=
formatFunc
(
value
as
number
,
decimals
);
const
handleNoValueValue
=
formattedValue
||
'no value'
;
return
`
${
prefix
&&
prefix
+
' '
}${
handleNoValueValue
}${
suffix
&&
' '
+
suffix
}
`
;
}
getFontColor
(
value
:
TimeSeriesValue
)
{
const
{
thresholds
,
theme
}
=
this
.
props
;
if
(
thresholds
.
length
===
1
)
{
return
getColorFromHexRgbOrName
(
thresholds
[
0
].
color
,
theme
.
type
);
}
const
atThreshold
=
thresholds
.
filter
(
threshold
=>
(
value
as
number
)
===
threshold
.
value
)[
0
];
if
(
atThreshold
)
{
return
getColorFromHexRgbOrName
(
atThreshold
.
color
,
theme
.
type
);
}
const
belowThreshold
=
thresholds
.
filter
(
threshold
=>
(
value
as
number
)
>
threshold
.
value
);
if
(
belowThreshold
.
length
>
0
)
{
const
nearestThreshold
=
belowThreshold
.
sort
((
t1
,
t2
)
=>
t2
.
value
-
t1
.
value
)[
0
];
return
getColorFromHexRgbOrName
(
nearestThreshold
.
color
,
theme
.
type
);
}
return
BasicGaugeColor
.
Red
;
}
getFormattedThresholds
()
{
const
{
maxValue
,
minValue
,
thresholds
,
theme
}
=
this
.
props
;
...
...
@@ -123,15 +67,13 @@ export class Gauge extends PureComponent<Props> {
draw
()
{
const
{
maxValue
,
minValue
,
showThresholdLabels
,
showThresholdMarkers
,
width
,
height
,
theme
,
value
}
=
this
.
props
;
const
formattedValue
=
this
.
formatValue
(
value
)
as
string
;
const
dimension
=
Math
.
min
(
width
,
height
*
1.3
);
const
backgroundColor
=
theme
.
type
===
GrafanaThemeType
.
Light
?
'rgb(230,230,230)'
:
theme
.
colors
.
dark3
;
const
gaugeWidthReduceRatio
=
showThresholdLabels
?
1.5
:
1
;
const
gaugeWidth
=
Math
.
min
(
dimension
/
6
,
60
)
/
gaugeWidthReduceRatio
;
const
thresholdMarkersWidth
=
gaugeWidth
/
5
;
const
fontSize
=
Math
.
min
(
dimension
/
5
,
100
)
*
(
formattedValue
!==
null
?
this
.
getFontScale
(
formattedValue
.
length
)
:
1
);
const
fontSize
=
Math
.
min
(
dimension
/
5
,
100
)
*
this
.
getFontScale
(
value
.
text
.
length
);
const
thresholdLabelFontSize
=
fontSize
/
2.5
;
const
options
=
{
...
...
@@ -160,9 +102,9 @@ export class Gauge extends PureComponent<Props> {
width
:
thresholdMarkersWidth
,
},
value
:
{
color
:
this
.
getFontColor
(
value
)
,
color
:
value
.
color
?
value
.
color
:
BasicGaugeColor
.
Red
,
formatter
:
()
=>
{
return
formattedValue
;
return
value
.
text
;
},
font
:
{
size
:
fontSize
,
family
:
'"Helvetica Neue", Helvetica, Arial, sans-serif'
},
},
...
...
@@ -171,7 +113,8 @@ export class Gauge extends PureComponent<Props> {
},
};
const
plotSeries
=
{
data
:
[[
0
,
value
]]
};
const
numeric
=
value
.
numeric
!==
null
?
value
.
numeric
:
0
;
const
plotSeries
=
{
data
:
[[
0
,
numeric
]]
};
try
{
$
.
plot
(
this
.
canvasElement
,
[
plotSeries
],
options
);
...
...
packages/grafana-ui/src/utils/valueProcessor.test.ts
0 → 100644
View file @
8cd54c94
import
{
getValueProcessor
,
getColorFromThreshold
}
from
'./valueProcessor'
;
import
{
getTheme
}
from
'../themes/index'
;
import
{
GrafanaThemeType
}
from
'../types/theme'
;
import
{
MappingType
,
ValueMapping
}
from
'../types/panel'
;
describe
(
'Process values'
,
()
=>
{
const
basicConversions
=
[
{
value
:
null
,
text
:
''
},
{
value
:
undefined
,
text
:
''
},
{
value
:
1.23
,
text
:
'1.23'
},
{
value
:
1
,
text
:
'1'
},
{
value
:
'hello'
,
text
:
'hello'
},
{
value
:
{},
text
:
'[object Object]'
},
{
value
:
[],
text
:
''
},
{
value
:
[
1
,
2
,
3
],
text
:
'1,2,3'
},
{
value
:
[
'a'
,
'b'
,
'c'
],
text
:
'a,b,c'
},
];
it
(
'should return return a string for any input value'
,
()
=>
{
const
processor
=
getValueProcessor
();
basicConversions
.
forEach
(
item
=>
{
expect
(
processor
(
item
.
value
).
text
).
toBe
(
item
.
text
);
});
});
it
(
'should add a suffix to any value'
,
()
=>
{
const
processor
=
getValueProcessor
({
prefix
:
'xxx'
,
theme
:
getTheme
(
GrafanaThemeType
.
Dark
),
});
basicConversions
.
forEach
(
item
=>
{
expect
(
processor
(
item
.
value
).
text
).
toBe
(
'xxx'
+
item
.
text
);
});
});
});
describe
(
'Get color from threshold'
,
()
=>
{
it
(
'should get first threshold color when only one threshold'
,
()
=>
{
const
thresholds
=
[{
index
:
0
,
value
:
-
Infinity
,
color
:
'#7EB26D'
}];
expect
(
getColorFromThreshold
(
49
,
thresholds
)).
toEqual
(
'#7EB26D'
);
});
it
(
'should get the threshold color if value is same as a threshold'
,
()
=>
{
const
thresholds
=
[
{
index
:
2
,
value
:
75
,
color
:
'#6ED0E0'
},
{
index
:
1
,
value
:
50
,
color
:
'#EAB839'
},
{
index
:
0
,
value
:
-
Infinity
,
color
:
'#7EB26D'
},
];
expect
(
getColorFromThreshold
(
50
,
thresholds
)).
toEqual
(
'#EAB839'
);
});
it
(
'should get the nearest threshold color between thresholds'
,
()
=>
{
const
thresholds
=
[
{
index
:
2
,
value
:
75
,
color
:
'#6ED0E0'
},
{
index
:
1
,
value
:
50
,
color
:
'#EAB839'
},
{
index
:
0
,
value
:
-
Infinity
,
color
:
'#7EB26D'
},
];
expect
(
getColorFromThreshold
(
55
,
thresholds
)).
toEqual
(
'#EAB839'
);
});
});
describe
(
'Format value'
,
()
=>
{
it
(
'should return if value isNaN'
,
()
=>
{
const
valueMappings
:
ValueMapping
[]
=
[];
const
value
=
'N/A'
;
const
instance
=
getValueProcessor
({
mappings
:
valueMappings
});
const
result
=
instance
(
value
);
expect
(
result
.
text
).
toEqual
(
'N/A'
);
});
it
(
'should return formatted value if there are no value mappings'
,
()
=>
{
const
valueMappings
:
ValueMapping
[]
=
[];
const
value
=
'6'
;
const
instance
=
getValueProcessor
({
mappings
:
valueMappings
,
decimals
:
1
});
const
result
=
instance
(
value
);
expect
(
result
.
text
).
toEqual
(
'6.0'
);
});
it
(
'should return formatted value if there are no matching value mappings'
,
()
=>
{
const
valueMappings
:
ValueMapping
[]
=
[
{
id
:
0
,
operator
:
''
,
text
:
'elva'
,
type
:
MappingType
.
ValueToText
,
value
:
'11'
},
{
id
:
1
,
operator
:
''
,
text
:
'1-9'
,
type
:
MappingType
.
RangeToText
,
from
:
'1'
,
to
:
'9'
},
];
const
value
=
'10'
;
const
instance
=
getValueProcessor
({
mappings
:
valueMappings
,
decimals
:
1
});
const
result
=
instance
(
value
);
expect
(
result
.
text
).
toEqual
(
'10.0'
);
});
it
(
'should return mapped value if there are matching value mappings'
,
()
=>
{
const
valueMappings
:
ValueMapping
[]
=
[
{
id
:
0
,
operator
:
''
,
text
:
'1-20'
,
type
:
MappingType
.
RangeToText
,
from
:
'1'
,
to
:
'20'
},
{
id
:
1
,
operator
:
''
,
text
:
'elva'
,
type
:
MappingType
.
ValueToText
,
value
:
'11'
},
];
const
value
=
'11'
;
const
instance
=
getValueProcessor
({
mappings
:
valueMappings
,
decimals
:
1
});
expect
(
instance
(
value
).
text
).
toEqual
(
'1-20'
);
});
});
packages/grafana-ui/src/utils/valueProcessor.ts
0 → 100644
View file @
8cd54c94
import
{
ValueMapping
,
Threshold
}
from
'../types/panel'
;
import
_
from
'lodash'
;
import
{
getValueFormat
,
DecimalCount
}
from
'./valueFormats/valueFormats'
;
import
{
getMappedValue
}
from
'./valueMappings'
;
import
{
GrafanaTheme
,
GrafanaThemeType
}
from
'../types/theme'
;
import
{
getColorFromHexRgbOrName
}
from
'./namedColorsPalette'
;
export
interface
DisplayValue
{
text
:
string
;
// How the value should be displayed
numeric
?:
number
;
// the value as a number
color
?:
string
;
// suggested color
}
export
interface
DisplayValueOptions
{
unit
?:
string
;
decimals
?:
DecimalCount
;
scaledDecimals
?:
DecimalCount
;
isUtc
?:
boolean
;
color
?:
string
;
mappings
?:
ValueMapping
[];
thresholds
?:
Threshold
[];
prefix
?:
string
;
suffix
?:
string
;
noValue
?:
string
;
theme
?:
GrafanaTheme
;
// Will pick 'dark' if not defined
}
export
type
ValueProcessor
=
(
value
:
any
)
=>
DisplayValue
;
export
function
getValueProcessor
(
options
?:
DisplayValueOptions
):
ValueProcessor
{
if
(
options
&&
!
_
.
isEmpty
(
options
))
{
const
formatFunc
=
getValueFormat
(
options
.
unit
||
'none'
);
return
(
value
:
any
)
=>
{
const
{
prefix
,
suffix
,
mappings
,
thresholds
,
theme
}
=
options
;
let
color
=
options
.
color
;
let
text
=
_
.
toString
(
value
);
const
numeric
=
_
.
toNumber
(
value
);
if
(
mappings
&&
mappings
.
length
>
0
)
{
const
mappedValue
=
getMappedValue
(
mappings
,
value
);
if
(
mappedValue
)
{
text
=
mappedValue
.
text
;
// TODO? convert the mapped value back to a number?
}
}
if
(
_
.
isNumber
(
numeric
))
{
text
=
formatFunc
(
numeric
,
options
.
decimals
,
options
.
scaledDecimals
,
options
.
isUtc
);
if
(
thresholds
&&
thresholds
.
length
>
0
)
{
color
=
getColorFromThreshold
(
numeric
,
thresholds
,
theme
);
}
}
if
(
!
text
)
{
text
=
options
.
noValue
?
options
.
noValue
:
''
;
}
if
(
prefix
)
{
text
=
prefix
+
text
;
}
if
(
suffix
)
{
text
=
text
+
suffix
;
}
return
{
text
,
numeric
,
color
};
};
}
return
toStringProcessor
;
}
function
toStringProcessor
(
value
:
any
):
DisplayValue
{
return
{
text
:
_
.
toString
(
value
),
numeric
:
_
.
toNumber
(
value
)
};
}
export
function
getColorFromThreshold
(
value
:
number
,
thresholds
:
Threshold
[],
theme
?:
GrafanaTheme
):
string
{
const
themeType
=
theme
?
theme
.
type
:
GrafanaThemeType
.
Dark
;
if
(
thresholds
.
length
===
1
)
{
return
getColorFromHexRgbOrName
(
thresholds
[
0
].
color
,
themeType
);
}
const
atThreshold
=
thresholds
.
filter
(
threshold
=>
value
===
threshold
.
value
)[
0
];
if
(
atThreshold
)
{
return
getColorFromHexRgbOrName
(
atThreshold
.
color
,
themeType
);
}
const
belowThreshold
=
thresholds
.
filter
(
threshold
=>
value
>
threshold
.
value
);
if
(
belowThreshold
.
length
>
0
)
{
const
nearestThreshold
=
belowThreshold
.
sort
((
t1
,
t2
)
=>
t2
.
value
-
t1
.
value
)[
0
];
return
getColorFromHexRgbOrName
(
nearestThreshold
.
color
,
themeType
);
}
// Use the first threshold as the default color
return
getColorFromHexRgbOrName
(
thresholds
[
0
].
color
,
themeType
);
}
public/app/plugins/panel/gauge/DisplayValueEditor.tsx
0 → 100644
View file @
8cd54c94
// Libraries
import
React
,
{
PureComponent
}
from
'react'
;
// Components
import
{
FormField
,
FormLabel
,
PanelOptionsGroup
,
UnitPicker
}
from
'@grafana/ui'
;
// Types
import
{
DisplayValueOptions
}
from
'@grafana/ui/src/utils/valueProcessor'
;
const
labelWidth
=
6
;
export
interface
Props
{
options
:
DisplayValueOptions
;
onChange
:
(
options
:
DisplayValueOptions
)
=>
void
;
}
export
class
DisplayValueEditor
extends
PureComponent
<
Props
>
{
onUnitChange
=
unit
=>
this
.
props
.
onChange
({
...
this
.
props
.
options
,
unit
:
unit
.
value
});
onDecimalChange
=
event
=>
{
if
(
!
isNaN
(
event
.
target
.
value
))
{
this
.
props
.
onChange
({
...
this
.
props
.
options
,
decimals
:
parseInt
(
event
.
target
.
value
,
10
),
});
}
else
{
this
.
props
.
onChange
({
...
this
.
props
.
options
,
decimals
:
null
,
});
}
};
onPrefixChange
=
event
=>
this
.
props
.
onChange
({
...
this
.
props
.
options
,
prefix
:
event
.
target
.
value
});
onSuffixChange
=
event
=>
this
.
props
.
onChange
({
...
this
.
props
.
options
,
suffix
:
event
.
target
.
value
});
render
()
{
const
{
unit
,
decimals
,
prefix
,
suffix
}
=
this
.
props
.
options
;
let
decimalsString
=
''
;
if
(
Number
.
isFinite
(
decimals
))
{
decimalsString
=
decimals
.
toString
();
}
return
(
<
PanelOptionsGroup
title=
"Display Value"
>
<
div
className=
"gf-form"
>
<
FormLabel
width=
{
labelWidth
}
>
Unit
</
FormLabel
>
<
UnitPicker
defaultValue=
{
unit
}
onChange=
{
this
.
onUnitChange
}
/>
</
div
>
<
FormField
label=
"Decimals"
labelWidth=
{
labelWidth
}
placeholder=
"auto"
onChange=
{
this
.
onDecimalChange
}
value=
{
decimalsString
}
type=
"number"
/>
<
FormField
label=
"Prefix"
labelWidth=
{
labelWidth
}
onChange=
{
this
.
onPrefixChange
}
value=
{
prefix
||
''
}
/>
<
FormField
label=
"Suffix"
labelWidth=
{
labelWidth
}
onChange=
{
this
.
onSuffixChange
}
value=
{
suffix
||
''
}
/>
</
PanelOptionsGroup
>
);
}
}
public/app/plugins/panel/gauge/GaugePanel.tsx
View file @
8cd54c94
...
...
@@ -9,30 +9,50 @@ import { Gauge } from '@grafana/ui';
// Types
import
{
GaugeOptions
}
from
'./types'
;
import
{
PanelProps
,
NullValueMode
,
TimeSeriesValue
}
from
'@grafana/ui/src/types'
;
import
{
PanelProps
,
NullValueMode
,
BasicGaugeColor
}
from
'@grafana/ui/src/types'
;
import
{
DisplayValue
,
getValueProcessor
}
from
'@grafana/ui/src/utils/valueProcessor'
;
interface
Props
extends
PanelProps
<
GaugeOptions
>
{}
interface
State
{
value
:
TimeSeries
Value
;
value
:
Display
Value
;
}
export
class
GaugePanel
extends
Component
<
Props
,
State
>
{
constructor
(
props
:
Props
)
{
super
(
props
);
if
(
props
.
options
.
valueOptions
)
{
console
.
warn
(
'TODO!! how do we best migration options?'
);
}
this
.
state
=
{
value
:
this
.
findValue
(
props
),
value
:
this
.
find
Display
Value
(
props
),
};
}
componentDidUpdate
(
prevProps
:
Props
)
{
if
(
this
.
props
.
panelData
!==
prevProps
.
panelData
)
{
this
.
setState
({
value
:
this
.
findValue
(
this
.
props
)
});
this
.
setState
({
value
:
this
.
find
Display
Value
(
this
.
props
)
});
}
}
findDisplayValue
(
props
:
Props
):
DisplayValue
{
const
{
replaceVariables
,
options
}
=
this
.
props
;
const
{
displayOptions
}
=
options
;
const
prefix
=
replaceVariables
(
displayOptions
.
prefix
);
const
suffix
=
replaceVariables
(
displayOptions
.
suffix
);
return
getValueProcessor
({
color
:
BasicGaugeColor
.
Red
,
// The default color
...
displayOptions
,
prefix
,
suffix
,
// ??? theme:getTheme(GrafanaThemeType.Dark), !! how do I get it here???
})(
this
.
findValue
(
props
));
}
findValue
(
props
:
Props
):
number
|
null
{
const
{
panelData
,
options
}
=
props
;
const
{
valueOptions
}
=
options
;
if
(
panelData
.
timeSeries
)
{
const
vmSeries
=
processTimeSeries
({
...
...
@@ -41,7 +61,7 @@ export class GaugePanel extends Component<Props, State> {
});
if
(
vmSeries
[
0
])
{
return
vmSeries
[
0
].
stats
[
valueO
ptions
.
stat
];
return
vmSeries
[
0
].
stats
[
o
ptions
.
stat
];
}
}
else
if
(
panelData
.
tableData
)
{
return
panelData
.
tableData
.
rows
[
0
].
find
(
prop
=>
prop
>
0
);
...
...
@@ -50,12 +70,9 @@ export class GaugePanel extends Component<Props, State> {
}
render
()
{
const
{
width
,
height
,
replaceVariables
,
options
}
=
this
.
props
;
const
{
valueOptions
}
=
options
;
const
{
width
,
height
,
options
}
=
this
.
props
;
const
{
value
}
=
this
.
state
;
const
prefix
=
replaceVariables
(
valueOptions
.
prefix
);
const
suffix
=
replaceVariables
(
valueOptions
.
suffix
);
return
(
<
ThemeContext
.
Consumer
>
{
theme
=>
(
...
...
@@ -63,12 +80,7 @@ export class GaugePanel extends Component<Props, State> {
value=
{
value
}
width=
{
width
}
height=
{
height
}
prefix=
{
prefix
}
suffix=
{
suffix
}
unit=
{
valueOptions
.
unit
}
decimals=
{
valueOptions
.
decimals
}
thresholds=
{
options
.
thresholds
}
valueMappings=
{
options
.
valueMappings
}
showThresholdLabels=
{
options
.
showThresholdLabels
}
showThresholdMarkers=
{
options
.
showThresholdMarkers
}
minValue=
{
options
.
minValue
}
...
...
public/app/plugins/panel/gauge/GaugePanelEditor.tsx
View file @
8cd54c94
...
...
@@ -11,6 +11,8 @@ import {
import
{
SingleStatValueEditor
}
from
'app/plugins/panel/gauge/SingleStatValueEditor'
;
import
{
GaugeOptionsBox
}
from
'./GaugeOptionsBox'
;
import
{
GaugeOptions
,
SingleStatValueOptions
}
from
'./types'
;
import
{
DisplayValueEditor
}
from
'./DisplayValueEditor'
;
import
{
DisplayValueOptions
}
from
'@grafana/ui/src/utils/valueProcessor'
;
export
class
GaugePanelEditor
extends
PureComponent
<
PanelEditorProps
<
GaugeOptions
>>
{
onThresholdsChanged
=
(
thresholds
:
Threshold
[])
=>
...
...
@@ -31,13 +33,22 @@ export class GaugePanelEditor extends PureComponent<PanelEditorProps<GaugeOption
valueOptions
,
});
onDisplayOptionsChanged
=
(
displayOptions
:
DisplayValueOptions
)
=>
this
.
props
.
onOptionsChange
({
...
this
.
props
.
options
,
displayOptions
,
});
render
()
{
const
{
onOptionsChange
,
options
}
=
this
.
props
;
return
(
<>
<
PanelOptionsGrid
>
<
SingleStatValueEditor
onChange=
{
this
.
onValueOptionsChanged
}
options=
{
options
.
valueOptions
}
/>
{
/* This just sets the 'stats', that should be moved to somethign more general */
}
<
SingleStatValueEditor
onChange=
{
onOptionsChange
}
options=
{
options
}
/>
<
DisplayValueEditor
onChange=
{
this
.
onDisplayOptionsChanged
}
options=
{
options
.
displayOptions
}
/>
<
GaugeOptionsBox
onOptionsChange=
{
onOptionsChange
}
options=
{
options
}
/>
<
ThresholdsEditor
onChange=
{
this
.
onThresholdsChanged
}
thresholds=
{
options
.
thresholds
}
/>
</
PanelOptionsGrid
>
...
...
public/app/plugins/panel/gauge/SingleStatValueEditor.tsx
View file @
8cd54c94
...
...
@@ -2,10 +2,10 @@
import
React
,
{
PureComponent
}
from
'react'
;
// Components
import
{
Form
Field
,
FormLabel
,
PanelOptionsGroup
,
Select
,
UnitPicker
}
from
'@grafana/ui'
;
import
{
Form
Label
,
PanelOptionsGroup
,
Select
}
from
'@grafana/ui'
;
// Types
import
{
SingleStatValu
eOptions
}
from
'./types'
;
import
{
Gaug
eOptions
}
from
'./types'
;
const
statOptions
=
[
{
value
:
'min'
,
label
:
'Min'
},
...
...
@@ -24,41 +24,18 @@ const statOptions = [
const
labelWidth
=
6
;
export
interface
Props
{
options
:
SingleStatValu
eOptions
;
onChange
:
(
valueOptions
:
SingleStatValu
eOptions
)
=>
void
;
options
:
Gaug
eOptions
;
onChange
:
(
options
:
Gaug
eOptions
)
=>
void
;
}
export
class
SingleStatValueEditor
extends
PureComponent
<
Props
>
{
onUnitChange
=
unit
=>
this
.
props
.
onChange
({
...
this
.
props
.
options
,
unit
:
unit
.
value
});
onStatChange
=
stat
=>
this
.
props
.
onChange
({
...
this
.
props
.
options
,
stat
:
stat
.
value
});
onDecimalChange
=
event
=>
{
if
(
!
isNaN
(
event
.
target
.
value
))
{
this
.
props
.
onChange
({
...
this
.
props
.
options
,
decimals
:
parseInt
(
event
.
target
.
value
,
10
),
});
}
else
{
this
.
props
.
onChange
({
...
this
.
props
.
options
,
decimals
:
null
,
});
}
};
onPrefixChange
=
event
=>
this
.
props
.
onChange
({
...
this
.
props
.
options
,
prefix
:
event
.
target
.
value
});
onSuffixChange
=
event
=>
this
.
props
.
onChange
({
...
this
.
props
.
options
,
suffix
:
event
.
target
.
value
});
render
()
{
const
{
stat
,
unit
,
decimals
,
prefix
,
suffix
}
=
this
.
props
.
options
;
let
decimalsString
=
''
;
if
(
Number
.
isFinite
(
decimals
))
{
decimalsString
=
decimals
.
toString
();
}
const
{
stat
}
=
this
.
props
.
options
;
return
(
<
PanelOptionsGroup
title=
"Value"
>
<
PanelOptionsGroup
title=
"
Show
Value"
>
<
div
className=
"gf-form"
>
<
FormLabel
width=
{
labelWidth
}
>
Stat
</
FormLabel
>
<
Select
...
...
@@ -68,20 +45,6 @@ export class SingleStatValueEditor extends PureComponent<Props> {
value=
{
statOptions
.
find
(
option
=>
option
.
value
===
stat
)
}
/>
</
div
>
<
div
className=
"gf-form"
>
<
FormLabel
width=
{
labelWidth
}
>
Unit
</
FormLabel
>
<
UnitPicker
defaultValue=
{
unit
}
onChange=
{
this
.
onUnitChange
}
/>
</
div
>
<
FormField
label=
"Decimals"
labelWidth=
{
labelWidth
}
placeholder=
"auto"
onChange=
{
this
.
onDecimalChange
}
value=
{
decimalsString
}
type=
"number"
/>
<
FormField
label=
"Prefix"
labelWidth=
{
labelWidth
}
onChange=
{
this
.
onPrefixChange
}
value=
{
prefix
||
''
}
/>
<
FormField
label=
"Suffix"
labelWidth=
{
labelWidth
}
onChange=
{
this
.
onSuffixChange
}
value=
{
suffix
||
''
}
/>
</
PanelOptionsGroup
>
);
}
...
...
public/app/plugins/panel/gauge/types.ts
View file @
8cd54c94
import
{
Threshold
,
ValueMapping
}
from
'@grafana/ui'
;
import
{
DisplayValueOptions
}
from
'@grafana/ui/src/utils/valueProcessor'
;
export
interface
GaugeOptions
{
valueMappings
:
ValueMapping
[];
maxValue
:
number
;
minValue
:
number
;
showThresholdLabels
:
boolean
;
showThresholdMarkers
:
boolean
;
thresholds
:
Threshold
[];
valueOptions
:
SingleStatValueOptions
;
stat
:
string
;
displayOptions
:
DisplayValueOptions
;
// TODO: migrate to DisplayValueOptions
thresholds
?:
Threshold
[];
valueMappings
?:
ValueMapping
[];
valueOptions
?:
SingleStatValueOptions
;
}
/** Deprecated -- migrate to */
export
interface
SingleStatValueOptions
{
unit
:
string
;
suffix
:
string
;
...
...
@@ -23,13 +30,14 @@ export const defaults: GaugeOptions = {
maxValue
:
100
,
showThresholdMarkers
:
true
,
showThresholdLabels
:
false
,
valueOptions
:
{
stat
:
'avg'
,
displayOptions
:
{
prefix
:
''
,
suffix
:
''
,
decimals
:
null
,
stat
:
'avg'
,
unit
:
'none'
,
},
valueMappings
:
[],
mappings
:
[],
thresholds
:
[],
},
};
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment