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
8994f3c2
Unverified
Commit
8994f3c2
authored
Dec 13, 2018
by
Torkel Ödegaard
Committed by
GitHub
Dec 13, 2018
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #14461 from grafana/gauge-value-mappings
Gauge value mappings
parents
4dbfcde7
80a2c3b1
Hide whitespace changes
Inline
Side-by-side
Showing
16 changed files
with
559 additions
and
41 deletions
+559
-41
public/app/core/components/Picker/SimplePicker.tsx
+3
-3
public/app/core/components/Picker/Unit/UnitPicker.tsx
+1
-1
public/app/plugins/panel/gauge/MappingRow.tsx
+155
-0
public/app/plugins/panel/gauge/Threshold.test.tsx
+16
-9
public/app/plugins/panel/gauge/Thresholds.tsx
+2
-11
public/app/plugins/panel/gauge/ValueMappings.test.tsx
+73
-0
public/app/plugins/panel/gauge/ValueMappings.tsx
+100
-0
public/app/plugins/panel/gauge/__snapshots__/ValueMappings.test.tsx.snap
+61
-0
public/app/plugins/panel/gauge/module.tsx
+28
-4
public/app/types/index.ts
+5
-1
public/app/types/panel.ts
+27
-0
public/app/viz/Gauge.tsx
+47
-10
public/sass/_grafana.scss
+1
-0
public/sass/components/_form_select_box.scss
+1
-1
public/sass/components/_thresholds.scss
+2
-1
public/sass/components/_value-mappings.scss
+37
-0
No files found.
public/app/core/components/Picker/SimplePicker.tsx
View file @
8994f3c2
...
@@ -11,7 +11,7 @@ interface Props {
...
@@ -11,7 +11,7 @@ interface Props {
onSelected
:
(
item
:
any
)
=>
{}
|
void
;
onSelected
:
(
item
:
any
)
=>
{}
|
void
;
options
:
any
[];
options
:
any
[];
placeholder
?:
string
;
placeholder
?:
string
;
width
:
number
;
width
?
:
number
;
value
:
any
;
value
:
any
;
}
}
...
@@ -28,8 +28,8 @@ const SimplePicker: SFC<Props> = ({
...
@@ -28,8 +28,8 @@ const SimplePicker: SFC<Props> = ({
})
=>
{
})
=>
{
return
(
return
(
<
Select
<
Select
classNamePrefix=
{
`gf-form-select-box`
}
classNamePrefix=
"gf-form-select-box"
className=
{
`
width-${width
} gf-form-input gf-form-input--form-dropdown ${className || ''}`
}
className=
{
`
${width ? 'width-' + width : ''
} gf-form-input gf-form-input--form-dropdown ${className || ''}`
}
components=
{
{
components=
{
{
Option
:
DescriptionOption
,
Option
:
DescriptionOption
,
}
}
}
}
...
...
public/app/core/components/Picker/Unit/UnitPicker.tsx
View file @
8994f3c2
...
@@ -63,7 +63,7 @@ export default class UnitPicker extends PureComponent<Props> {
...
@@ -63,7 +63,7 @@ export default class UnitPicker extends PureComponent<Props> {
return
(
return
(
<
Select
<
Select
classNamePrefix=
"gf-form-select-box"
classNamePrefix=
"gf-form-select-box"
className=
{
`width-${width} gf-form-input--form-dropdown`
}
className=
{
`width-${width} gf-form-input
gf-form-input
--form-dropdown`
}
defaultValue=
{
value
}
defaultValue=
{
value
}
isSearchable=
{
true
}
isSearchable=
{
true
}
menuShouldScrollIntoView=
{
false
}
menuShouldScrollIntoView=
{
false
}
...
...
public/app/plugins/panel/gauge/MappingRow.tsx
0 → 100644
View file @
8994f3c2
import
React
,
{
PureComponent
}
from
'react'
;
import
{
Label
}
from
'app/core/components/Label/Label'
;
import
SimplePicker
from
'app/core/components/Picker/SimplePicker'
;
import
{
MappingType
,
RangeMap
,
ValueMap
}
from
'app/types'
;
interface
Props
{
mapping
:
ValueMap
|
RangeMap
;
updateMapping
:
(
mapping
)
=>
void
;
removeMapping
:
()
=>
void
;
}
interface
State
{
from
:
string
;
id
:
number
;
operator
:
string
;
text
:
string
;
to
:
string
;
type
:
MappingType
;
value
:
string
;
}
const
mappingOptions
=
[
{
value
:
MappingType
.
ValueToText
,
label
:
'Value'
},
{
value
:
MappingType
.
RangeToText
,
label
:
'Range'
},
];
export
default
class
MappingRow
extends
PureComponent
<
Props
,
State
>
{
constructor
(
props
)
{
super
(
props
);
this
.
state
=
{
...
props
.
mapping
,
};
}
onMappingValueChange
=
event
=>
{
this
.
setState
({
value
:
event
.
target
.
value
});
};
onMappingFromChange
=
event
=>
{
this
.
setState
({
from
:
event
.
target
.
value
});
};
onMappingToChange
=
event
=>
{
this
.
setState
({
to
:
event
.
target
.
value
});
};
onMappingTextChange
=
event
=>
{
this
.
setState
({
text
:
event
.
target
.
value
});
};
onMappingTypeChange
=
mappingType
=>
{
this
.
setState
({
type
:
mappingType
});
};
updateMapping
=
()
=>
{
this
.
props
.
updateMapping
({
...
this
.
state
});
};
renderRow
()
{
const
{
from
,
text
,
to
,
type
,
value
}
=
this
.
state
;
if
(
type
===
MappingType
.
RangeToText
)
{
return
(
<
div
className=
"gf-form"
>
<
div
className=
"gf-form-inline mapping-row-input"
>
<
Label
width=
{
4
}
>
From
</
Label
>
<
div
>
<
input
className=
"gf-form-input"
value=
{
from
}
onBlur=
{
this
.
updateMapping
}
onChange=
{
this
.
onMappingFromChange
}
/>
</
div
>
</
div
>
<
div
className=
"gf-form-inline mapping-row-input"
>
<
Label
width=
{
4
}
>
To
</
Label
>
<
div
>
<
input
className=
"gf-form-input"
value=
{
to
}
onBlur=
{
this
.
updateMapping
}
onChange=
{
this
.
onMappingToChange
}
/>
</
div
>
</
div
>
<
div
className=
"gf-form-inline mapping-row-input"
>
<
Label
width=
{
4
}
>
Text
</
Label
>
<
div
>
<
input
className=
"gf-form-input"
value=
{
text
}
onBlur=
{
this
.
updateMapping
}
onChange=
{
this
.
onMappingTextChange
}
/>
</
div
>
</
div
>
</
div
>
);
}
return
(
<
div
className=
"gf-form"
>
<
div
className=
"gf-form-inline mapping-row-input"
>
<
Label
width=
{
4
}
>
Value
</
Label
>
<
div
>
<
input
className=
"gf-form-input"
onBlur=
{
this
.
updateMapping
}
onChange=
{
this
.
onMappingValueChange
}
value=
{
value
}
/>
</
div
>
</
div
>
<
div
className=
"gf-form-inline mapping-row-input"
>
<
Label
width=
{
4
}
>
Text
</
Label
>
<
div
>
<
input
className=
"gf-form-input"
onBlur=
{
this
.
updateMapping
}
value=
{
text
}
onChange=
{
this
.
onMappingTextChange
}
/>
</
div
>
</
div
>
</
div
>
);
}
render
()
{
const
{
type
}
=
this
.
state
;
return
(
<
div
className=
"mapping-row"
>
<
div
className=
"gf-form-inline mapping-row-type"
>
<
Label
width=
{
5
}
>
Type
</
Label
>
<
SimplePicker
placeholder=
"Choose type"
options=
{
mappingOptions
}
value=
{
mappingOptions
.
find
(
o
=>
o
.
value
===
type
)
}
getOptionLabel=
{
i
=>
i
.
label
}
getOptionValue=
{
i
=>
i
.
value
}
onSelected=
{
type
=>
this
.
onMappingTypeChange
(
type
.
value
)
}
width=
{
7
}
/>
</
div
>
<
div
>
{
this
.
renderRow
()
}
</
div
>
<
div
onClick=
{
this
.
props
.
removeMapping
}
className=
"threshold-row-remove"
>
<
i
className=
"fa fa-times"
/>
</
div
>
</
div
>
);
}
}
public/app/plugins/panel/gauge/Threshold.test.tsx
View file @
8994f3c2
import
React
from
'react'
;
import
React
from
'react'
;
import
{
shallow
}
from
'enzyme'
;
import
{
shallow
}
from
'enzyme'
;
import
Thresholds
from
'./Thresholds'
;
import
Thresholds
from
'./Thresholds'
;
import
{
OptionsProps
}
from
'./module'
;
import
{
defaultProps
,
OptionsProps
}
from
'./module'
;
import
{
PanelOptionsProps
}
from
'../../../types'
;
import
{
PanelOptionsProps
}
from
'../../../types'
;
const
setup
=
(
propOverrides
?:
object
)
=>
{
const
setup
=
(
propOverrides
?:
object
)
=>
{
const
props
:
PanelOptionsProps
<
OptionsProps
>
=
{
const
props
:
PanelOptionsProps
<
OptionsProps
>
=
{
onChange
:
jest
.
fn
(),
onChange
:
jest
.
fn
(),
options
:
{}
as
OptionsProps
,
options
:
{
...
defaultProps
.
options
,
thresholds
:
[
{
index
:
0
,
label
:
'Min'
,
value
:
0
,
canRemove
:
false
,
color
:
'rgba(50, 172, 45, 0.97)'
},
{
index
:
1
,
label
:
'Max'
,
value
:
100
,
canRemove
:
false
},
],
},
};
};
Object
.
assign
(
props
,
propOverrides
);
Object
.
assign
(
props
,
propOverrides
);
...
@@ -15,12 +21,6 @@ const setup = (propOverrides?: object) => {
...
@@ -15,12 +21,6 @@ const setup = (propOverrides?: object) => {
return
shallow
(<
Thresholds
{
...
props
}
/>).
instance
()
as
Thresholds
;
return
shallow
(<
Thresholds
{
...
props
}
/>).
instance
()
as
Thresholds
;
};
};
const
thresholds
=
[
{
index
:
0
,
label
:
'Min'
,
value
:
0
,
canRemove
:
false
,
color
:
'rgba(50, 172, 45, 0.97)'
},
{
index
:
1
,
label
:
''
,
value
:
50
,
canRemove
:
true
,
color
:
'rgba(237, 129, 40, 0.89)'
},
{
index
:
2
,
label
:
'Max'
,
value
:
100
,
canRemove
:
false
},
];
describe
(
'Add threshold'
,
()
=>
{
describe
(
'Add threshold'
,
()
=>
{
it
(
'should add threshold between min and max'
,
()
=>
{
it
(
'should add threshold between min and max'
,
()
=>
{
const
instance
=
setup
();
const
instance
=
setup
();
...
@@ -36,7 +36,14 @@ describe('Add threshold', () => {
...
@@ -36,7 +36,14 @@ describe('Add threshold', () => {
it
(
'should add threshold between min and added threshold'
,
()
=>
{
it
(
'should add threshold between min and added threshold'
,
()
=>
{
const
instance
=
setup
({
const
instance
=
setup
({
options
:
{
thresholds
:
thresholds
},
options
:
{
...
defaultProps
.
options
,
thresholds
:
[
{
index
:
0
,
label
:
'Min'
,
value
:
0
,
canRemove
:
false
,
color
:
'rgba(50, 172, 45, 0.97)'
},
{
index
:
1
,
label
:
''
,
value
:
50
,
canRemove
:
true
,
color
:
'rgba(237, 129, 40, 0.89)'
},
{
index
:
2
,
label
:
'Max'
,
value
:
100
,
canRemove
:
false
},
],
},
});
});
instance
.
onAddThreshold
(
1
);
instance
.
onAddThreshold
(
1
);
...
...
public/app/plugins/panel/gauge/Thresholds.tsx
View file @
8994f3c2
...
@@ -2,27 +2,18 @@ import React, { PureComponent } from 'react';
...
@@ -2,27 +2,18 @@ import React, { PureComponent } from 'react';
import
classNames
from
'classnames/bind'
;
import
classNames
from
'classnames/bind'
;
import
{
ColorPicker
}
from
'app/core/components/colorpicker/ColorPicker'
;
import
{
ColorPicker
}
from
'app/core/components/colorpicker/ColorPicker'
;
import
{
OptionModuleProps
}
from
'./module'
;
import
{
OptionModuleProps
}
from
'./module'
;
import
{
Threshold
}
from
'app/types'
;
import
{
BasicGaugeColor
,
Threshold
}
from
'app/types'
;
interface
State
{
interface
State
{
thresholds
:
Threshold
[];
thresholds
:
Threshold
[];
}
}
enum
BasicGaugeColor
{
Green
=
'rgba(50, 172, 45, 0.97)'
,
Orange
=
'rgba(237, 129, 40, 0.89)'
,
Red
=
'rgb(212, 74, 58)'
,
}
export
default
class
Thresholds
extends
PureComponent
<
OptionModuleProps
,
State
>
{
export
default
class
Thresholds
extends
PureComponent
<
OptionModuleProps
,
State
>
{
constructor
(
props
)
{
constructor
(
props
)
{
super
(
props
);
super
(
props
);
this
.
state
=
{
this
.
state
=
{
thresholds
:
this
.
props
.
options
.
thresholds
||
[
thresholds
:
props
.
options
.
thresholds
,
{
index
:
0
,
label
:
'Min'
,
value
:
0
,
canRemove
:
false
,
color
:
BasicGaugeColor
.
Green
},
{
index
:
1
,
label
:
'Max'
,
value
:
100
,
canRemove
:
false
},
],
};
};
}
}
...
...
public/app/plugins/panel/gauge/ValueMappings.test.tsx
0 → 100644
View file @
8994f3c2
import
React
from
'react'
;
import
{
shallow
}
from
'enzyme'
;
import
ValueMappings
from
'./ValueMappings'
;
import
{
defaultProps
,
OptionModuleProps
}
from
'./module'
;
import
{
MappingType
}
from
'app/types'
;
const
setup
=
(
propOverrides
?:
object
)
=>
{
const
props
:
OptionModuleProps
=
{
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'
},
],
},
};
Object
.
assign
(
props
,
propOverrides
);
const
wrapper
=
shallow
(<
ValueMappings
{
...
props
}
/>);
const
instance
=
wrapper
.
instance
()
as
ValueMappings
;
return
{
instance
,
wrapper
,
};
};
describe
(
'Render'
,
()
=>
{
it
(
'should render component'
,
()
=>
{
const
{
wrapper
}
=
setup
();
expect
(
wrapper
).
toMatchSnapshot
();
});
});
describe
(
'On remove mapping'
,
()
=>
{
it
(
'Should remove mapping with id 0'
,
()
=>
{
const
{
instance
}
=
setup
();
instance
.
onRemoveMapping
(
1
);
expect
(
instance
.
state
.
mappings
).
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
([
{
id
:
1
,
operator
:
''
,
type
:
MappingType
.
ValueToText
,
value
:
'20'
,
text
:
'Ok'
},
]);
});
});
describe
(
'Next id to add'
,
()
=>
{
it
(
'should be 4'
,
()
=>
{
const
{
instance
}
=
setup
();
instance
.
addMapping
();
expect
(
instance
.
state
.
nextIdToAdd
).
toEqual
(
4
);
});
it
(
'should default to 1'
,
()
=>
{
const
{
instance
}
=
setup
({
options
:
{
...
defaultProps
.
options
}
});
expect
(
instance
.
state
.
nextIdToAdd
).
toEqual
(
1
);
});
});
public/app/plugins/panel/gauge/ValueMappings.tsx
0 → 100644
View file @
8994f3c2
import
React
,
{
PureComponent
}
from
'react'
;
import
MappingRow
from
'./MappingRow'
;
import
{
OptionModuleProps
}
from
'./module'
;
import
{
MappingType
,
RangeMap
,
ValueMap
}
from
'app/types'
;
interface
State
{
mappings
:
Array
<
ValueMap
|
RangeMap
>
;
nextIdToAdd
:
number
;
}
export
default
class
ValueMappings
extends
PureComponent
<
OptionModuleProps
,
State
>
{
constructor
(
props
)
{
super
(
props
);
const
mappings
=
props
.
options
.
mappings
;
this
.
state
=
{
mappings
:
mappings
||
[],
nextIdToAdd
:
mappings
.
length
>
0
?
this
.
getMaxIdFromMappings
(
mappings
)
:
1
,
};
}
getMaxIdFromMappings
(
mappings
)
{
return
Math
.
max
.
apply
(
null
,
mappings
.
map
(
mapping
=>
mapping
.
id
).
map
(
m
=>
m
))
+
1
;
}
addMapping
=
()
=>
this
.
setState
(
prevState
=>
({
mappings
:
[
...
prevState
.
mappings
,
{
id
:
prevState
.
nextIdToAdd
,
operator
:
''
,
value
:
''
,
text
:
''
,
type
:
MappingType
.
ValueToText
,
from
:
''
,
to
:
''
,
},
],
nextIdToAdd
:
prevState
.
nextIdToAdd
+
1
,
}));
onRemoveMapping
=
id
=>
{
this
.
setState
(
prevState
=>
({
mappings
:
prevState
.
mappings
.
filter
(
m
=>
{
return
m
.
id
!==
id
;
}),
}),
()
=>
{
this
.
props
.
onChange
({
...
this
.
props
.
options
,
mappings
:
this
.
state
.
mappings
});
}
);
};
updateGauge
=
mapping
=>
{
this
.
setState
(
prevState
=>
({
mappings
:
prevState
.
mappings
.
map
(
m
=>
{
if
(
m
.
id
===
mapping
.
id
)
{
return
{
...
mapping
};
}
return
m
;
}),
}),
()
=>
{
this
.
props
.
onChange
({
...
this
.
props
.
options
,
mappings
:
this
.
state
.
mappings
});
}
);
};
render
()
{
const
{
mappings
}
=
this
.
state
;
return
(
<
div
className=
"section gf-form-group"
>
<
h5
className=
"page-heading"
>
Value mappings
</
h5
>
<
div
>
{
mappings
.
length
>
0
&&
mappings
.
map
((
mapping
,
index
)
=>
(
<
MappingRow
key=
{
`${mapping.text}-${index}`
}
mapping=
{
mapping
}
updateMapping=
{
this
.
updateGauge
}
removeMapping=
{
()
=>
this
.
onRemoveMapping
(
mapping
.
id
)
}
/>
))
}
</
div
>
<
div
className=
"add-mapping-row"
onClick=
{
this
.
addMapping
}
>
<
div
className=
"add-mapping-row-icon"
>
<
i
className=
"fa fa-plus"
/>
</
div
>
<
div
className=
"add-mapping-row-label"
>
Add mapping
</
div
>
</
div
>
</
div
>
);
}
}
public/app/plugins/panel/gauge/__snapshots__/ValueMappings.test.tsx.snap
0 → 100644
View file @
8994f3c2
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`Render should render component 1`] = `
<div
className="section gf-form-group"
>
<h5
className="page-heading"
>
Value mappings
</h5>
<div>
<MappingRow
key="Ok-0"
mapping={
Object {
"id": 1,
"operator": "",
"text": "Ok",
"type": 1,
"value": "20",
}
}
removeMapping={[Function]}
updateMapping={[Function]}
/>
<MappingRow
key="Meh-1"
mapping={
Object {
"from": "21",
"id": 2,
"operator": "",
"text": "Meh",
"to": "30",
"type": 2,
}
}
removeMapping={[Function]}
updateMapping={[Function]}
/>
</div>
<div
className="add-mapping-row"
onClick={[Function]}
>
<div
className="add-mapping-row-icon"
>
<i
className="fa fa-plus"
/>
</div>
<div
className="add-mapping-row-label"
>
Add mapping
</div>
</div>
</div>
`;
public/app/plugins/panel/gauge/module.tsx
View file @
8994f3c2
import
React
,
{
PureComponent
}
from
'react'
;
import
React
,
{
PureComponent
}
from
'react'
;
import
Gauge
from
'app/viz/Gauge'
;
import
Gauge
from
'app/viz/Gauge'
;
import
{
NullValueMode
,
PanelOptionsProps
,
PanelProps
,
Threshold
}
from
'app/types'
;
import
{
getTimeSeriesVMs
}
from
'app/viz/state/timeSeries'
;
import
{
getTimeSeriesVMs
}
from
'app/viz/state/timeSeries'
;
import
ValueOptions
from
'./ValueOptions'
;
import
ValueOptions
from
'./ValueOptions'
;
import
GaugeOptions
from
'./GaugeOptions'
;
import
GaugeOptions
from
'./GaugeOptions'
;
import
Thresholds
from
'./Thresholds'
;
import
Thresholds
from
'./Thresholds'
;
import
ValueMappings
from
'./ValueMappings'
;
import
{
BasicGaugeColor
,
NullValueMode
,
PanelOptionsProps
,
PanelProps
,
RangeMap
,
Threshold
,
ValueMap
,
}
from
'app/types'
;
export
interface
OptionsProps
{
export
interface
OptionsProps
{
decimals
:
number
;
decimals
:
number
;
...
@@ -15,6 +24,7 @@ export interface OptionsProps {
...
@@ -15,6 +24,7 @@ export interface OptionsProps {
suffix
:
string
;
suffix
:
string
;
unit
:
string
;
unit
:
string
;
thresholds
:
Threshold
[];
thresholds
:
Threshold
[];
mappings
:
Array
<
RangeMap
|
ValueMap
>
;
}
}
export
interface
OptionModuleProps
{
export
interface
OptionModuleProps
{
...
@@ -30,6 +40,14 @@ export const defaultProps = {
...
@@ -30,6 +40,14 @@ export const defaultProps = {
showThresholdMarkers
:
true
,
showThresholdMarkers
:
true
,
showThresholdLabels
:
false
,
showThresholdLabels
:
false
,
suffix
:
''
,
suffix
:
''
,
decimals
:
0
,
stat
:
''
,
unit
:
''
,
mappings
:
[],
thresholds
:
[
{
index
:
0
,
label
:
'Min'
,
value
:
0
,
canRemove
:
false
,
color
:
BasicGaugeColor
.
Green
},
{
index
:
1
,
label
:
'Max'
,
value
:
100
,
canRemove
:
false
},
],
},
},
};
};
...
@@ -52,11 +70,17 @@ class Options extends PureComponent<PanelOptionsProps<OptionsProps>> {
...
@@ -52,11 +70,17 @@ class Options extends PureComponent<PanelOptionsProps<OptionsProps>> {
static
defaultProps
=
defaultProps
;
static
defaultProps
=
defaultProps
;
render
()
{
render
()
{
const
{
onChange
,
options
}
=
this
.
props
;
return
(
return
(
<
div
>
<
div
>
<
ValueOptions
onChange=
{
this
.
props
.
onChange
}
options=
{
this
.
props
.
options
}
/>
<
div
className=
"form-section"
>
<
GaugeOptions
onChange=
{
this
.
props
.
onChange
}
options=
{
this
.
props
.
options
}
/>
<
ValueOptions
onChange=
{
onChange
}
options=
{
options
}
/>
<
Thresholds
onChange=
{
this
.
props
.
onChange
}
options=
{
this
.
props
.
options
}
/>
<
GaugeOptions
onChange=
{
onChange
}
options=
{
options
}
/>
<
Thresholds
onChange=
{
onChange
}
options=
{
options
}
/>
</
div
>
<
div
className=
"form-section"
>
<
ValueMappings
onChange=
{
onChange
}
options=
{
options
}
/>
</
div
>
</
div
>
</
div
>
);
);
}
}
...
...
public/app/types/index.ts
View file @
8994f3c2
...
@@ -21,7 +21,7 @@ import {
...
@@ -21,7 +21,7 @@ import {
DataQueryOptions
,
DataQueryOptions
,
IntervalValues
,
IntervalValues
,
}
from
'./series'
;
}
from
'./series'
;
import
{
PanelProps
,
PanelOptionsProps
,
Threshold
}
from
'./panel'
;
import
{
BasicGaugeColor
,
MappingType
,
PanelProps
,
PanelOptionsProps
,
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
{
...
@@ -93,7 +93,11 @@ export {
...
@@ -93,7 +93,11 @@ export {
Threshold
,
Threshold
,
ValidationEvents
,
ValidationEvents
,
ValidationRule
,
ValidationRule
,
ValueMap
,
RangeMap
,
IntervalValues
,
IntervalValues
,
MappingType
,
BasicGaugeColor
,
};
};
export
interface
StoreState
{
export
interface
StoreState
{
...
...
public/app/types/panel.ts
View file @
8994f3c2
...
@@ -36,3 +36,30 @@ export interface Threshold {
...
@@ -36,3 +36,30 @@ export interface Threshold {
color
?:
string
;
color
?:
string
;
canRemove
:
boolean
;
canRemove
:
boolean
;
}
}
export
enum
MappingType
{
ValueToText
=
1
,
RangeToText
=
2
,
}
export
enum
BasicGaugeColor
{
Green
=
'rgba(50, 172, 45, 0.97)'
,
Orange
=
'rgba(237, 129, 40, 0.89)'
,
Red
=
'rgb(212, 74, 58)'
,
}
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
;
}
public/app/viz/Gauge.tsx
View file @
8994f3c2
import
React
,
{
PureComponent
}
from
'react'
;
import
React
,
{
PureComponent
}
from
'react'
;
import
$
from
'jquery'
;
import
$
from
'jquery'
;
import
{
Threshold
,
TimeSeriesVMs
}
from
'app/types'
;
import
{
MappingType
,
RangeMap
,
Threshold
,
TimeSeriesVMs
,
ValueMap
}
from
'app/types'
;
import
config
from
'../core/config'
;
import
config
from
'../core/config'
;
import
kbn
from
'../core/utils/kbn'
;
import
kbn
from
'../core/utils/kbn'
;
interface
Props
{
interface
Props
{
decimals
:
number
;
decimals
:
number
;
height
:
number
;
mappings
:
Array
<
RangeMap
|
ValueMap
>
;
maxValue
:
number
;
minValue
:
number
;
prefix
:
string
;
timeSeries
:
TimeSeriesVMs
;
timeSeries
:
TimeSeriesVMs
;
showThresholdMarkers
:
boolean
;
thresholds
:
Threshold
[];
thresholds
:
Threshold
[];
showThresholdMarkers
:
boolean
;
showThresholdLabels
:
boolean
;
showThresholdLabels
:
boolean
;
unit
:
string
;
width
:
number
;
height
:
number
;
stat
:
string
;
stat
:
string
;
prefix
:
string
;
suffix
:
string
;
suffix
:
string
;
unit
:
string
;
width
:
number
;
}
}
export
class
Gauge
extends
PureComponent
<
Props
>
{
export
class
Gauge
extends
PureComponent
<
Props
>
{
canvasElement
:
any
;
canvasElement
:
any
;
static
defaultProps
=
{
static
defaultProps
=
{
minValue
:
0
,
maxValue
:
100
,
maxValue
:
100
,
mappings
:
[],
minValue
:
0
,
prefix
:
''
,
prefix
:
''
,
showThresholdMarkers
:
true
,
showThresholdMarkers
:
true
,
showThresholdLabels
:
false
,
showThresholdLabels
:
false
,
suffix
:
''
,
suffix
:
''
,
unit
:
'none'
,
thresholds
:
[
thresholds
:
[
{
label
:
'Min'
,
value
:
0
,
color
:
'rgba(50, 172, 45, 0.97)'
},
{
label
:
'Min'
,
value
:
0
,
color
:
'rgba(50, 172, 45, 0.97)'
},
{
label
:
'Max'
,
value
:
100
,
color
:
'rgba(245, 54, 54, 0.9)'
},
{
label
:
'Max'
,
value
:
100
,
color
:
'rgba(245, 54, 54, 0.9)'
},
],
],
unit
:
'none'
,
};
};
componentDidMount
()
{
componentDidMount
()
{
...
@@ -43,16 +47,49 @@ export class Gauge extends PureComponent<Props> {
...
@@ -43,16 +47,49 @@ export class Gauge extends PureComponent<Props> {
this
.
draw
();
this
.
draw
();
}
}
formatWithMappings
(
mappings
,
value
)
{
const
valueMaps
=
mappings
.
filter
(
m
=>
m
.
type
===
MappingType
.
ValueToText
);
const
rangeMaps
=
mappings
.
filter
(
m
=>
m
.
type
===
MappingType
.
RangeToText
);
const
valueMap
=
valueMaps
.
map
(
mapping
=>
{
if
(
mapping
.
value
&&
value
===
mapping
.
value
)
{
return
mapping
.
text
;
}
})[
0
];
const
rangeMap
=
rangeMaps
.
map
(
mapping
=>
{
if
(
mapping
.
from
&&
mapping
.
to
&&
value
>
mapping
.
from
&&
value
<
mapping
.
to
)
{
return
mapping
.
text
;
}
})[
0
];
return
{
rangeMap
,
valueMap
,
};
}
formatValue
(
value
)
{
formatValue
(
value
)
{
const
{
decimals
,
prefix
,
suffix
,
unit
}
=
this
.
props
;
const
{
decimals
,
mappings
,
prefix
,
suffix
,
unit
}
=
this
.
props
;
const
formatFunc
=
kbn
.
valueFormats
[
unit
];
const
formatFunc
=
kbn
.
valueFormats
[
unit
];
const
formattedValue
=
formatFunc
(
value
,
decimals
);
if
(
mappings
.
length
>
0
)
{
const
{
rangeMap
,
valueMap
}
=
this
.
formatWithMappings
(
mappings
,
formattedValue
);
if
(
valueMap
)
{
return
valueMap
;
}
else
if
(
rangeMap
)
{
return
rangeMap
;
}
}
if
(
isNaN
(
value
))
{
if
(
isNaN
(
value
))
{
return
'-'
;
return
'-'
;
}
}
return
`
${
prefix
}
${
format
Func
(
value
,
decimals
)
}
${
suffix
}
`
;
return
`
${
prefix
}
${
format
tedValue
}
${
suffix
}
`
;
}
}
draw
()
{
draw
()
{
...
...
public/sass/_grafana.scss
View file @
8994f3c2
...
@@ -106,6 +106,7 @@
...
@@ -106,6 +106,7 @@
@import
'components/unit-picker'
;
@import
'components/unit-picker'
;
@import
'components/thresholds'
;
@import
'components/thresholds'
;
@import
'components/toggle_button_group'
;
@import
'components/toggle_button_group'
;
@import
'components/value-mappings'
;
// PAGES
// PAGES
@import
'pages/login'
;
@import
'pages/login'
;
...
...
public/sass/components/_form_select_box.scss
View file @
8994f3c2
...
@@ -90,7 +90,7 @@ $select-input-bg-disabled: $input-bg-disabled;
...
@@ -90,7 +90,7 @@ $select-input-bg-disabled: $input-bg-disabled;
.gf-form-select-box__value-container
{
.gf-form-select-box__value-container
{
display
:
table-cell
;
display
:
table-cell
;
padding
:
8
px
10px
;
padding
:
6
px
10px
;
>
div
{
>
div
{
display
:
inline-block
;
display
:
inline-block
;
}
}
...
...
public/sass/components/_thresholds.scss
View file @
8994f3c2
...
@@ -77,7 +77,8 @@
...
@@ -77,7 +77,8 @@
display
:
flex
;
display
:
flex
;
align-items
:
center
;
align-items
:
center
;
justify-content
:
center
;
justify-content
:
center
;
width
:
36px
;
height
:
37px
;
width
:
37px
;
cursor
:
pointer
;
cursor
:
pointer
;
}
}
...
...
public/sass/components/_value-mappings.scss
0 → 100644
View file @
8994f3c2
.mapping-row
{
display
:
flex
;
margin-bottom
:
10px
;
}
.mapping-row-type
{
margin-right
:
5px
;
}
.mapping-row-input
{
margin-right
:
5px
;
}
.add-mapping-row
{
display
:
flex
;
overflow
:
hidden
;
height
:
37px
;
cursor
:
pointer
;
border-radius
:
$border-radius
;
width
:
200px
;
}
.add-mapping-row-icon
{
display
:
flex
;
align-items
:
center
;
justify-content
:
center
;
width
:
36px
;
background-color
:
$green
;
}
.add-mapping-row-label
{
align-items
:
center
;
display
:
flex
;
padding
:
5px
8px
;
background-color
:
$input-label-bg
;
width
:
calc
(
100%
-
36px
);
}
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