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
14caa6a0
Unverified
Commit
14caa6a0
authored
Jul 12, 2019
by
Ryan McKinley
Committed by
GitHub
Jul 12, 2019
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
FieldDisplay: move threshold and mapping to Field (#17043)
parent
acc678c3
Hide whitespace changes
Inline
Side-by-side
Showing
31 changed files
with
517 additions
and
348 deletions
+517
-348
packages/grafana-data/src/types/data.ts
+9
-0
packages/grafana-data/src/types/threshold.ts
+0
-1
packages/grafana-data/src/utils/thresholds.ts
+17
-18
packages/grafana-ui/src/components/BarGauge/BarGauge.story.tsx
+3
-3
packages/grafana-ui/src/components/BarGauge/BarGauge.test.tsx
+1
-5
packages/grafana-ui/src/components/BarGauge/BarGauge.tsx
+9
-3
packages/grafana-ui/src/components/Gauge/Gauge.test.tsx
+4
-4
packages/grafana-ui/src/components/Gauge/Gauge.tsx
+3
-3
packages/grafana-ui/src/components/SingleStatShared/SingleStatBaseOptions.ts
+34
-10
packages/grafana-ui/src/components/ThresholdsEditor/ThresholdsEditor.test.tsx
+59
-60
packages/grafana-ui/src/components/ThresholdsEditor/ThresholdsEditor.tsx
+91
-91
packages/grafana-ui/src/components/ThresholdsEditor/__snapshots__/ThresholdsEditor.test.tsx.snap
+1
-2
packages/grafana-ui/src/utils/displayValue.test.ts
+4
-4
packages/grafana-ui/src/utils/displayValue.ts
+3
-5
packages/grafana-ui/src/utils/fieldDisplay.test.ts
+18
-11
packages/grafana-ui/src/utils/fieldDisplay.ts
+6
-16
public/app/features/dashboard/state/PanelModel.test.ts
+1
-16
public/app/features/dashboard/state/PanelModel.ts
+0
-11
public/app/plugins/panel/bargauge/BarGaugeMigrations.test.ts
+61
-0
public/app/plugins/panel/bargauge/BarGaugeMigrations.ts
+36
-0
public/app/plugins/panel/bargauge/BarGaugePanel.tsx
+1
-2
public/app/plugins/panel/bargauge/BarGaugePanelEditor.tsx
+14
-9
public/app/plugins/panel/bargauge/__snapshots__/BarGaugeMigrations.test.ts.snap
+36
-0
public/app/plugins/panel/bargauge/module.tsx
+3
-1
public/app/plugins/panel/gauge/GaugeMigrations.ts
+29
-12
public/app/plugins/panel/gauge/GaugePanel.tsx
+1
-2
public/app/plugins/panel/gauge/GaugePanelEditor.tsx
+14
-9
public/app/plugins/panel/gauge/__snapshots__/GaugeMigrations.test.ts.snap
+29
-33
public/app/plugins/panel/piechart/PieChartPanelEditor.tsx
+8
-5
public/app/plugins/panel/singlestat2/SingleStatEditor.tsx
+14
-9
public/app/plugins/panel/singlestat2/types.ts
+8
-3
No files found.
packages/grafana-data/src/types/data.ts
View file @
14caa6a0
import
{
Threshold
}
from
'./threshold'
;
import
{
ValueMapping
}
from
'./valueMapping'
;
export
enum
LoadingState
{
NotStarted
=
'NotStarted'
,
Loading
=
'Loading'
,
...
...
@@ -49,6 +52,12 @@ export interface Field {
decimals
?:
number
|
null
;
// Significant digits (for display)
min
?:
number
|
null
;
max
?:
number
|
null
;
// Convert input values into a display value
mappings
?:
ValueMapping
[];
// Must be sorted by 'value', first value is always -Infinity
thresholds
?:
Threshold
[];
}
export
interface
Labels
{
...
...
packages/grafana-data/src/types/threshold.ts
View file @
14caa6a0
export
interface
Threshold
{
index
:
number
;
value
:
number
;
color
:
string
;
}
packages/grafana-data/src/utils/thresholds.ts
View file @
14caa6a0
import
{
Threshold
}
from
'../types'
;
export
function
getThresholdForValue
(
thresholds
:
Threshold
[],
value
:
number
|
null
|
string
|
undefined
):
Threshold
|
null
{
if
(
thresholds
.
length
===
1
)
{
return
thresholds
[
0
];
}
const
atThreshold
=
thresholds
.
filter
(
threshold
=>
(
value
as
number
)
===
threshold
.
value
)[
0
];
if
(
atThreshold
)
{
return
atThreshold
;
}
const
belowThreshold
=
thresholds
.
filter
(
threshold
=>
(
value
as
number
)
>
threshold
.
value
);
if
(
belowThreshold
.
length
>
0
)
{
const
nearestThreshold
=
belowThreshold
.
sort
((
t1
:
Threshold
,
t2
:
Threshold
)
=>
t2
.
value
-
t1
.
value
)[
0
];
return
nearestThreshold
;
export
function
getActiveThreshold
(
value
:
number
,
thresholds
:
Threshold
[]):
Threshold
{
let
active
=
thresholds
[
0
];
for
(
const
threshold
of
thresholds
)
{
if
(
value
>=
threshold
.
value
)
{
active
=
threshold
;
}
else
{
break
;
}
}
return
active
;
}
return
null
;
/**
* Sorts the thresholds
*/
export
function
sortThresholds
(
thresholds
:
Threshold
[])
{
return
thresholds
.
sort
((
t1
,
t2
)
=>
{
return
t1
.
value
-
t2
.
value
;
});
}
packages/grafana-ui/src/components/BarGauge/BarGauge.story.tsx
View file @
14caa6a0
...
...
@@ -49,9 +49,9 @@ function addBarGaugeStory(name: string, overrides: Partial<Props>) {
orientation
:
VizOrientation
.
Vertical
,
displayMode
:
'basic'
,
thresholds
:
[
{
index
:
0
,
value
:
-
Infinity
,
color
:
'green'
},
{
index
:
1
,
value
:
threshold1Value
,
color
:
threshold1Color
},
{
index
:
1
,
value
:
threshold2Value
,
color
:
threshold2Color
},
{
value
:
-
Infinity
,
color
:
'green'
},
{
value
:
threshold1Value
,
color
:
threshold1Color
},
{
value
:
threshold2Value
,
color
:
threshold2Color
},
],
};
...
...
packages/grafana-ui/src/components/BarGauge/BarGauge.test.tsx
View file @
14caa6a0
...
...
@@ -25,11 +25,7 @@ function getProps(propOverrides?: Partial<Props>): Props {
maxValue
:
100
,
minValue
:
0
,
displayMode
:
'basic'
,
thresholds
:
[
{
index
:
0
,
value
:
-
Infinity
,
color
:
'green'
},
{
index
:
1
,
value
:
70
,
color
:
'orange'
},
{
index
:
2
,
value
:
90
,
color
:
'red'
},
],
thresholds
:
[{
value
:
-
Infinity
,
color
:
'green'
},
{
value
:
70
,
color
:
'orange'
},
{
value
:
90
,
color
:
'red'
}],
height
:
300
,
width
:
300
,
value
:
{
...
...
packages/grafana-ui/src/components/BarGauge/BarGauge.tsx
View file @
14caa6a0
...
...
@@ -7,7 +7,7 @@ import { getColorFromHexRgbOrName } from '../../utils';
// Types
import
{
DisplayValue
,
Themeable
,
VizOrientation
}
from
'../../types'
;
import
{
Threshold
,
TimeSeriesValue
,
get
ThresholdForValue
}
from
'@grafana/data'
;
import
{
Threshold
,
TimeSeriesValue
,
get
ActiveThreshold
}
from
'@grafana/data'
;
const
MIN_VALUE_HEIGHT
=
18
;
const
MAX_VALUE_HEIGHT
=
50
;
...
...
@@ -87,8 +87,14 @@ export class BarGauge extends PureComponent<Props> {
getCellColor
(
positionValue
:
TimeSeriesValue
):
CellColors
{
const
{
thresholds
,
theme
,
value
}
=
this
.
props
;
const
activeThreshold
=
getThresholdForValue
(
thresholds
,
positionValue
);
if
(
positionValue
===
null
)
{
return
{
background
:
'gray'
,
border
:
'gray'
,
};
}
const
activeThreshold
=
getActiveThreshold
(
positionValue
,
thresholds
);
if
(
activeThreshold
!==
null
)
{
const
color
=
getColorFromHexRgbOrName
(
activeThreshold
.
color
,
theme
.
type
);
...
...
@@ -474,7 +480,7 @@ export function getBarGradient(props: Props, maxSize: number): string {
export
function
getValueColor
(
props
:
Props
):
string
{
const
{
thresholds
,
theme
,
value
}
=
props
;
const
activeThreshold
=
get
ThresholdForValue
(
thresholds
,
value
.
numeric
);
const
activeThreshold
=
get
ActiveThreshold
(
value
.
numeric
,
thresholds
);
if
(
activeThreshold
!==
null
)
{
return
getColorFromHexRgbOrName
(
activeThreshold
.
color
,
theme
.
type
);
...
...
packages/grafana-ui/src/components/Gauge/Gauge.test.tsx
View file @
14caa6a0
...
...
@@ -14,7 +14,7 @@ const setup = (propOverrides?: object) => {
minValue
:
0
,
showThresholdMarkers
:
true
,
showThresholdLabels
:
false
,
thresholds
:
[{
index
:
0
,
value
:
-
Infinity
,
color
:
'#7EB26D'
}],
thresholds
:
[{
value
:
-
Infinity
,
color
:
'#7EB26D'
}],
height
:
300
,
width
:
300
,
value
:
{
...
...
@@ -48,9 +48,9 @@ describe('Get thresholds formatted', () => {
it
(
'should get the correct formatted values when thresholds are added'
,
()
=>
{
const
{
instance
}
=
setup
({
thresholds
:
[
{
index
:
0
,
value
:
-
Infinity
,
color
:
'#7EB26D'
},
{
index
:
1
,
value
:
50
,
color
:
'#EAB839'
},
{
index
:
2
,
value
:
75
,
color
:
'#6ED0E0'
},
{
value
:
-
Infinity
,
color
:
'#7EB26D'
},
{
value
:
50
,
color
:
'#EAB839'
},
{
value
:
75
,
color
:
'#6ED0E0'
},
],
});
...
...
packages/grafana-ui/src/components/Gauge/Gauge.tsx
View file @
14caa6a0
...
...
@@ -43,12 +43,12 @@ export class Gauge extends PureComponent<Props> {
const
lastThreshold
=
thresholds
[
thresholds
.
length
-
1
];
return
[
...
thresholds
.
map
(
threshold
=>
{
if
(
threshold
.
index
===
0
)
{
...
thresholds
.
map
(
(
threshold
,
index
)
=>
{
if
(
index
===
0
)
{
return
{
value
:
minValue
,
color
:
getColorFromHexRgbOrName
(
threshold
.
color
,
theme
.
type
)
};
}
const
previousThreshold
=
thresholds
[
threshold
.
index
-
1
];
const
previousThreshold
=
thresholds
[
index
-
1
];
return
{
value
:
threshold
.
value
,
color
:
getColorFromHexRgbOrName
(
previousThreshold
.
color
,
theme
.
type
)
};
}),
{
value
:
maxValue
,
color
:
getColorFromHexRgbOrName
(
lastThreshold
.
color
,
theme
.
type
)
},
...
...
packages/grafana-ui/src/components/SingleStatShared/SingleStatBaseOptions.ts
View file @
14caa6a0
...
...
@@ -3,7 +3,7 @@ import omit from 'lodash/omit';
import
{
VizOrientation
,
PanelModel
}
from
'../../types/panel'
;
import
{
FieldDisplayOptions
}
from
'../../utils/fieldDisplay'
;
import
{
Field
,
getFieldReducers
}
from
'@grafana/data'
;
import
{
Field
,
getFieldReducers
,
Threshold
,
sortThresholds
}
from
'@grafana/data'
;
export
interface
SingleStatBaseOptions
{
fieldOptions
:
FieldDisplayOptions
;
...
...
@@ -39,18 +39,16 @@ export const sharedSingleStatMigrationCheck = (panel: PanelModel<SingleStatBaseO
const
{
valueOptions
}
=
old
;
const
fieldOptions
=
(
old
.
fieldOptions
=
{}
as
FieldDisplayOptions
);
fieldOptions
.
mappings
=
old
.
valueMappings
;
fieldOptions
.
thresholds
=
old
.
thresholds
;
const
field
=
(
fieldOptions
.
defaults
=
{}
as
Field
);
if
(
valueOptions
)
{
field
.
unit
=
valueOptions
.
unit
;
field
.
decimals
=
valueOptions
.
decimals
;
field
.
mappings
=
old
.
valueMappings
;
field
.
thresholds
=
migrateOldThresholds
(
old
.
thresholds
);
field
.
unit
=
valueOptions
.
unit
;
field
.
decimals
=
valueOptions
.
decimals
;
// Make sure the stats have a valid name
if
(
valueOptions
.
stat
)
{
fieldOptions
.
calcs
=
getFieldReducers
([
valueOptions
.
stat
]).
map
(
s
=>
s
.
id
);
}
// Make sure the stats have a valid name
if
(
valueOptions
.
stat
)
{
fieldOptions
.
calcs
=
getFieldReducers
([
valueOptions
.
stat
]).
map
(
s
=>
s
.
id
);
}
field
.
min
=
old
.
minValue
;
...
...
@@ -58,7 +56,33 @@ export const sharedSingleStatMigrationCheck = (panel: PanelModel<SingleStatBaseO
// remove old props
return
omit
(
old
,
'valueMappings'
,
'thresholds'
,
'valueOptions'
,
'minValue'
,
'maxValue'
);
}
else
if
(
old
.
fieldOptions
)
{
// Move mappins & thresholds to field defautls (6.4+)
const
{
mappings
,
thresholds
,
...
fieldOptions
}
=
old
.
fieldOptions
;
fieldOptions
.
defaults
=
{
mappings
,
thresholds
:
migrateOldThresholds
(
thresholds
),
...
fieldOptions
.
defaults
,
};
old
.
fieldOptions
=
fieldOptions
;
return
old
;
}
return
panel
.
options
;
};
export
function
migrateOldThresholds
(
thresholds
?:
any
[]):
Threshold
[]
|
undefined
{
if
(
!
thresholds
||
!
thresholds
.
length
)
{
return
undefined
;
}
const
copy
=
thresholds
.
map
(
t
=>
{
return
{
// Drops 'index'
value
:
t
.
value
===
null
?
-
Infinity
:
t
.
value
,
color
:
t
.
color
,
};
});
sortThresholds
(
copy
);
copy
[
0
].
value
=
-
Infinity
;
return
copy
;
}
packages/grafana-ui/src/components/ThresholdsEditor/ThresholdsEditor.test.tsx
View file @
14caa6a0
import
React
,
{
ChangeEvent
}
from
'react'
;
import
{
mount
}
from
'enzyme'
;
import
{
ThresholdsEditor
,
Props
}
from
'./ThresholdsEditor'
;
import
{
ThresholdsEditor
,
Props
,
threshodsWithoutKey
}
from
'./ThresholdsEditor'
;
import
{
colors
}
from
'../../utils'
;
const
setup
=
(
propOverrides
?:
Partial
<
Props
>
)
=>
{
...
...
@@ -20,6 +20,10 @@ const setup = (propOverrides?: Partial<Props>) => {
};
};
function
getCurrentThresholds
(
editor
:
ThresholdsEditor
)
{
return
threshodsWithoutKey
(
editor
.
state
.
thresholds
);
}
describe
(
'Render'
,
()
=>
{
it
(
'should render with base threshold'
,
()
=>
{
const
{
wrapper
}
=
setup
();
...
...
@@ -32,60 +36,55 @@ describe('Initialization', () => {
it
(
'should add a base threshold if missing'
,
()
=>
{
const
{
instance
}
=
setup
();
expect
(
instance
.
state
.
thresholds
).
toEqual
([{
index
:
0
,
value
:
-
Infinity
,
color
:
colors
[
0
]
}]);
expect
(
getCurrentThresholds
(
instance
)).
toEqual
([{
value
:
-
Infinity
,
color
:
colors
[
0
]
}]);
});
});
describe
(
'Add threshold'
,
()
=>
{
it
(
'should not add threshold at index 0'
,
()
=>
{
const
{
instance
}
=
setup
();
instance
.
onAddThreshold
(
0
);
expect
(
instance
.
state
.
thresholds
).
toEqual
([{
index
:
0
,
value
:
-
Infinity
,
color
:
colors
[
0
]
}]);
});
it
(
'should add threshold'
,
()
=>
{
const
{
instance
}
=
setup
();
instance
.
onAddThreshold
(
1
);
instance
.
onAddThreshold
After
(
instance
.
state
.
thresholds
[
0
]
);
expect
(
instance
.
state
.
thresholds
).
toEqual
([
{
index
:
0
,
value
:
-
Infinity
,
color
:
colors
[
0
]
},
{
index
:
1
,
value
:
50
,
color
:
colors
[
2
]
},
expect
(
getCurrentThresholds
(
instance
)
).
toEqual
([
{
value
:
-
Infinity
,
color
:
colors
[
0
]
},
// 0
{
value
:
50
,
color
:
colors
[
2
]
},
// 1
]);
});
it
(
'should add another threshold above a first'
,
()
=>
{
const
{
instance
}
=
setup
({
thresholds
:
[{
index
:
0
,
value
:
-
Infinity
,
color
:
colors
[
0
]
},
{
index
:
1
,
value
:
50
,
color
:
colors
[
2
]
}],
thresholds
:
[
{
value
:
-
Infinity
,
color
:
colors
[
0
]
},
// 0
{
value
:
50
,
color
:
colors
[
2
]
},
// 1
],
});
instance
.
onAddThreshold
(
2
);
instance
.
onAddThreshold
After
(
instance
.
state
.
thresholds
[
1
]
);
expect
(
instance
.
state
.
thresholds
).
toEqual
([
{
index
:
0
,
value
:
-
Infinity
,
color
:
colors
[
0
]
},
{
index
:
1
,
value
:
50
,
color
:
colors
[
2
]
},
{
index
:
2
,
value
:
75
,
color
:
colors
[
3
]
},
expect
(
getCurrentThresholds
(
instance
)
).
toEqual
([
{
value
:
-
Infinity
,
color
:
colors
[
0
]
},
// 0
{
value
:
50
,
color
:
colors
[
2
]
},
// 1
{
value
:
75
,
color
:
colors
[
3
]
},
// 2
]);
});
it
(
'should add another threshold between first and second index'
,
()
=>
{
const
{
instance
}
=
setup
({
thresholds
:
[
{
index
:
0
,
value
:
-
Infinity
,
color
:
colors
[
0
]
},
{
index
:
1
,
value
:
50
,
color
:
colors
[
2
]
},
{
index
:
2
,
value
:
75
,
color
:
colors
[
3
]
},
{
value
:
-
Infinity
,
color
:
colors
[
0
]
},
{
value
:
50
,
color
:
colors
[
2
]
},
{
value
:
75
,
color
:
colors
[
3
]
},
],
});
instance
.
onAddThreshold
(
2
);
instance
.
onAddThreshold
After
(
instance
.
state
.
thresholds
[
1
]
);
expect
(
instance
.
state
.
thresholds
).
toEqual
([
{
index
:
0
,
value
:
-
Infinity
,
color
:
colors
[
0
]
},
{
index
:
1
,
value
:
50
,
color
:
colors
[
2
]
},
{
index
:
2
,
value
:
62.5
,
color
:
colors
[
4
]
},
{
index
:
3
,
value
:
75
,
color
:
colors
[
3
]
},
expect
(
getCurrentThresholds
(
instance
)
).
toEqual
([
{
value
:
-
Infinity
,
color
:
colors
[
0
]
},
{
value
:
50
,
color
:
colors
[
2
]
},
{
value
:
62.5
,
color
:
colors
[
4
]
},
{
value
:
75
,
color
:
colors
[
3
]
},
]);
});
});
...
...
@@ -93,30 +92,30 @@ describe('Add threshold', () => {
describe
(
'Remove threshold'
,
()
=>
{
it
(
'should not remove threshold at index 0'
,
()
=>
{
const
thresholds
=
[
{
index
:
0
,
value
:
-
Infinity
,
color
:
'#7EB26D'
},
{
index
:
1
,
value
:
50
,
color
:
'#EAB839'
},
{
index
:
2
,
value
:
75
,
color
:
'#6ED0E0'
},
{
value
:
-
Infinity
,
color
:
'#7EB26D'
},
{
value
:
50
,
color
:
'#EAB839'
},
{
value
:
75
,
color
:
'#6ED0E0'
},
];
const
{
instance
}
=
setup
({
thresholds
});
instance
.
onRemoveThreshold
(
thresholds
[
0
]);
instance
.
onRemoveThreshold
(
instance
.
state
.
thresholds
[
0
]);
expect
(
instance
.
state
.
thresholds
).
toEqual
(
thresholds
);
expect
(
getCurrentThresholds
(
instance
)
).
toEqual
(
thresholds
);
});
it
(
'should remove threshold'
,
()
=>
{
const
thresholds
=
[
{
index
:
0
,
value
:
-
Infinity
,
color
:
'#7EB26D'
},
{
index
:
1
,
value
:
50
,
color
:
'#EAB839'
},
{
index
:
2
,
value
:
75
,
color
:
'#6ED0E0'
},
{
value
:
-
Infinity
,
color
:
'#7EB26D'
},
{
value
:
50
,
color
:
'#EAB839'
},
{
value
:
75
,
color
:
'#6ED0E0'
},
];
const
{
instance
}
=
setup
({
thresholds
});
instance
.
onRemoveThreshold
(
thresholds
[
1
]);
instance
.
onRemoveThreshold
(
instance
.
state
.
thresholds
[
1
]);
expect
(
instance
.
state
.
thresholds
).
toEqual
([
{
index
:
0
,
value
:
-
Infinity
,
color
:
'#7EB26D'
},
{
index
:
1
,
value
:
75
,
color
:
'#6ED0E0'
},
expect
(
getCurrentThresholds
(
instance
)
).
toEqual
([
{
value
:
-
Infinity
,
color
:
'#7EB26D'
},
{
value
:
75
,
color
:
'#6ED0E0'
},
]);
});
});
...
...
@@ -124,25 +123,25 @@ describe('Remove threshold', () => {
describe
(
'change threshold value'
,
()
=>
{
it
(
'should not change threshold at index 0'
,
()
=>
{
const
thresholds
=
[
{
index
:
0
,
value
:
-
Infinity
,
color
:
'#7EB26D'
},
{
index
:
1
,
value
:
50
,
color
:
'#EAB839'
},
{
index
:
2
,
value
:
75
,
color
:
'#6ED0E0'
},
{
value
:
-
Infinity
,
color
:
'#7EB26D'
},
{
value
:
50
,
color
:
'#EAB839'
},
{
value
:
75
,
color
:
'#6ED0E0'
},
];
const
{
instance
}
=
setup
({
thresholds
});
const
mockEvent
=
({
target
:
{
value
:
'12'
}
}
as
any
)
as
ChangeEvent
<
HTMLInputElement
>
;
instance
.
onChangeThresholdValue
(
mockEvent
,
thresholds
[
0
]);
instance
.
onChangeThresholdValue
(
mockEvent
,
instance
.
state
.
thresholds
[
0
]);
expect
(
instance
.
state
.
thresholds
).
toEqual
(
thresholds
);
expect
(
getCurrentThresholds
(
instance
)
).
toEqual
(
thresholds
);
});
it
(
'should update value'
,
()
=>
{
const
{
instance
}
=
setup
();
const
thresholds
=
[
{
index
:
0
,
value
:
-
Infinity
,
color
:
'#7EB26D'
},
{
index
:
1
,
value
:
50
,
color
:
'#EAB839'
},
{
index
:
2
,
value
:
75
,
color
:
'#6ED0E0'
},
{
value
:
-
Infinity
,
color
:
'#7EB26D'
,
key
:
1
},
{
value
:
50
,
color
:
'#EAB839'
,
key
:
2
},
{
value
:
75
,
color
:
'#6ED0E0'
,
key
:
3
},
];
instance
.
state
=
{
...
...
@@ -153,10 +152,10 @@ describe('change threshold value', () => {
instance
.
onChangeThresholdValue
(
mockEvent
,
thresholds
[
1
]);
expect
(
instance
.
state
.
thresholds
).
toEqual
([
{
index
:
0
,
value
:
-
Infinity
,
color
:
'#7EB26D'
},
{
index
:
1
,
value
:
78
,
color
:
'#EAB839'
},
{
index
:
2
,
value
:
75
,
color
:
'#6ED0E0'
},
expect
(
getCurrentThresholds
(
instance
)
).
toEqual
([
{
value
:
-
Infinity
,
color
:
'#7EB26D'
},
{
value
:
78
,
color
:
'#EAB839'
},
{
value
:
75
,
color
:
'#6ED0E0'
},
]);
});
});
...
...
@@ -165,9 +164,9 @@ describe('on blur threshold value', () => {
it
(
'should resort rows and update indexes'
,
()
=>
{
const
{
instance
}
=
setup
();
const
thresholds
=
[
{
index
:
0
,
value
:
-
Infinity
,
color
:
'#7EB26D'
},
{
index
:
1
,
value
:
78
,
color
:
'#EAB839'
},
{
index
:
2
,
value
:
75
,
color
:
'#6ED0E0'
},
{
value
:
-
Infinity
,
color
:
'#7EB26D'
,
key
:
1
},
{
value
:
78
,
color
:
'#EAB839'
,
key
:
2
},
{
value
:
75
,
color
:
'#6ED0E0'
,
key
:
3
},
];
instance
.
setState
({
...
...
@@ -176,10 +175,10 @@ describe('on blur threshold value', () => {
instance
.
onBlur
();
expect
(
instance
.
state
.
thresholds
).
toEqual
([
{
index
:
0
,
value
:
-
Infinity
,
color
:
'#7EB26D'
},
{
index
:
1
,
value
:
75
,
color
:
'#6ED0E0'
},
{
index
:
2
,
value
:
78
,
color
:
'#EAB839'
},
expect
(
getCurrentThresholds
(
instance
)
).
toEqual
([
{
value
:
-
Infinity
,
color
:
'#7EB26D'
},
{
value
:
75
,
color
:
'#6ED0E0'
},
{
value
:
78
,
color
:
'#EAB839'
},
]);
});
});
packages/grafana-ui/src/components/ThresholdsEditor/ThresholdsEditor.tsx
View file @
14caa6a0
import
React
,
{
PureComponent
,
ChangeEvent
}
from
'react'
;
import
{
Threshold
}
from
'@grafana/data'
;
import
{
Threshold
,
sortThresholds
}
from
'@grafana/data'
;
import
{
colors
}
from
'../../utils'
;
import
{
ThemeContext
}
from
'../../themes'
;
import
{
getColorFromHexRgbOrName
}
from
'../../utils'
;
...
...
@@ -13,115 +13,121 @@ export interface Props {
}
interface
State
{
thresholds
:
Threshold
[];
thresholds
:
ThresholdWithKey
[];
}
interface
ThresholdWithKey
extends
Threshold
{
key
:
number
;
}
let
counter
=
100
;
export
class
ThresholdsEditor
extends
PureComponent
<
Props
,
State
>
{
constructor
(
props
:
Props
)
{
super
(
props
);
const
addDefaultThreshold
=
this
.
props
.
thresholds
.
length
===
0
;
const
thresholds
:
Threshold
[]
=
addDefaultThreshold
?
[{
index
:
0
,
value
:
-
Infinity
,
color
:
colors
[
0
]
}]
:
props
.
thresholds
;
const
thresholds
=
props
.
thresholds
?
props
.
thresholds
.
map
(
t
=>
{
return
{
color
:
t
.
color
,
value
:
t
.
value
===
null
?
-
Infinity
:
t
.
value
,
key
:
counter
++
,
};
})
:
([]
as
ThresholdWithKey
[]);
let
needsCallback
=
false
;
if
(
!
thresholds
.
length
)
{
thresholds
.
push
({
value
:
-
Infinity
,
color
:
colors
[
0
],
key
:
counter
++
});
needsCallback
=
true
;
}
else
{
// First value is always base
thresholds
[
0
].
value
=
-
Infinity
;
}
// Update the state
this
.
state
=
{
thresholds
};
if
(
addDefaultThreshold
)
{
if
(
needsCallback
)
{
this
.
onChange
();
}
}
onAddThreshold
=
(
index
:
number
)
=>
{
onAddThreshold
After
=
(
threshold
:
ThresholdWithKey
)
=>
{
const
{
thresholds
}
=
this
.
state
;
const
maxValue
=
100
;
const
minValue
=
0
;
if
(
index
===
0
)
{
return
;
}
const
newThresholds
=
thresholds
.
map
(
threshold
=>
{
if
(
threshold
.
index
>=
index
)
{
const
index
=
threshold
.
index
+
1
;
threshold
=
{
...
threshold
,
index
};
let
prev
:
ThresholdWithKey
|
undefined
=
undefined
;
let
next
:
ThresholdWithKey
|
undefined
=
undefined
;
for
(
const
t
of
thresholds
)
{
if
(
prev
&&
prev
.
key
===
threshold
.
key
)
{
next
=
t
;
break
;
}
return
threshold
;
}
);
prev
=
t
;
}
// Setting value to a value between the previous thresholds
const
beforeThreshold
=
newThresholds
.
filter
(
t
=>
t
.
index
===
index
-
1
&&
t
.
index
!==
0
)[
0
];
const
afterThreshold
=
newThresholds
.
filter
(
t
=>
t
.
index
===
index
+
1
&&
t
.
index
!==
0
)[
0
];
const
beforeThresholdValue
=
beforeThreshold
!==
undefined
?
beforeThreshold
.
value
:
minValue
;
const
afterThresholdValue
=
afterThreshold
!==
undefined
?
afterThreshold
.
value
:
maxValue
;
const
value
=
afterThresholdValue
-
(
afterThresholdValue
-
beforeThresholdValue
)
/
2
;
const
prevValue
=
prev
&&
isFinite
(
prev
.
value
)
?
prev
.
value
:
minValue
;
const
nextValue
=
next
&&
isFinite
(
next
.
value
)
?
next
.
value
:
maxValue
;
// Set a color
const
color
=
colors
.
filter
(
c
=>
!
newThresholds
.
some
(
t
=>
t
.
color
===
c
))[
1
];
const
color
=
colors
.
filter
(
c
=>
!
thresholds
.
some
(
t
=>
t
.
color
===
c
))[
1
];
const
add
=
{
value
:
prevValue
+
(
nextValue
-
prevValue
)
/
2.0
,
color
:
color
,
key
:
counter
++
,
};
const
newThresholds
=
[...
thresholds
,
add
];
sortThresholds
(
newThresholds
);
this
.
setState
(
{
thresholds
:
this
.
sortThresholds
([
...
newThresholds
,
{
color
,
index
,
value
:
value
as
number
,
},
]),
thresholds
:
newThresholds
,
},
()
=>
this
.
onChange
()
);
};
onRemoveThreshold
=
(
threshold
:
Threshold
)
=>
{
if
(
threshold
.
index
===
0
)
{
onRemoveThreshold
=
(
threshold
:
ThresholdWithKey
)
=>
{
const
{
thresholds
}
=
this
.
state
;
if
(
!
thresholds
.
length
)
{
return
;
}
// Don't remove index 0
if
(
threshold
.
key
===
thresholds
[
0
].
key
)
{
return
;
}
this
.
setState
(
prevState
=>
{
const
newThresholds
=
prevState
.
thresholds
.
map
(
t
=>
{
if
(
t
.
index
>
threshold
.
index
)
{
const
index
=
t
.
index
-
1
;
t
=
{
...
t
,
index
};
}
return
t
;
});
return
{
thresholds
:
newThresholds
.
filter
(
t
=>
t
!==
threshold
),
};
{
thresholds
:
thresholds
.
filter
(
t
=>
t
.
key
!==
threshold
.
key
),
},
()
=>
this
.
onChange
()
);
};
onChangeThresholdValue
=
(
event
:
ChangeEvent
<
HTMLInputElement
>
,
threshold
:
Threshold
)
=>
{
if
(
threshold
.
index
===
0
)
{
return
;
}
const
{
thresholds
}
=
this
.
state
;
onChangeThresholdValue
=
(
event
:
ChangeEvent
<
HTMLInputElement
>
,
threshold
:
ThresholdWithKey
)
=>
{
const
cleanValue
=
event
.
target
.
value
.
replace
(
/,/g
,
'.'
);
const
parsedValue
=
parseFloat
(
cleanValue
);
const
value
=
isNaN
(
parsedValue
)
?
''
:
parsedValue
;
const
newThresholds
=
thresholds
.
map
(
t
=>
{
if
(
t
===
threshold
&&
t
.
index
!==
0
)
{
const
thresholds
=
this
.
state
.
thresholds
.
map
(
t
=>
{
if
(
t
.
key
===
threshold
.
key
)
{
t
=
{
...
t
,
value
:
value
as
number
};
}
return
t
;
});
this
.
setState
({
thresholds
:
newThresholds
});
if
(
thresholds
.
length
)
{
thresholds
[
0
].
value
=
-
Infinity
;
}
this
.
setState
({
thresholds
});
};
onChangeThresholdColor
=
(
threshold
:
Threshold
,
color
:
string
)
=>
{
onChangeThresholdColor
=
(
threshold
:
Threshold
WithKey
,
color
:
string
)
=>
{
const
{
thresholds
}
=
this
.
state
;
const
newThresholds
=
thresholds
.
map
(
t
=>
{
if
(
t
===
threshold
)
{
if
(
t
.
key
===
threshold
.
key
)
{
t
=
{
...
t
,
color
:
color
};
}
...
...
@@ -137,30 +143,22 @@ export class ThresholdsEditor extends PureComponent<Props, State> {
};
onBlur
=
()
=>
{
this
.
setState
(
prevState
=>
{
const
sortThresholds
=
this
.
sortThresholds
([...
prevState
.
thresholds
]);
let
index
=
0
;
sortThresholds
.
forEach
(
t
=>
{
t
.
index
=
index
++
;
});
return
{
thresholds
:
sortThresholds
};
});
this
.
onChange
();
const
thresholds
=
[...
this
.
state
.
thresholds
];
sortThresholds
(
thresholds
);
this
.
setState
(
{
thresholds
,
},
()
=>
this
.
onChange
()
);
};
onChange
=
()
=>
{
this
.
props
.
onChange
(
this
.
state
.
thresholds
);
};
sortThresholds
=
(
thresholds
:
Threshold
[])
=>
{
return
thresholds
.
sort
((
t1
,
t2
)
=>
{
return
t1
.
value
-
t2
.
value
;
});
const
{
thresholds
}
=
this
.
state
;
this
.
props
.
onChange
(
threshodsWithoutKey
(
thresholds
));
};
renderInput
=
(
threshold
:
Threshold
)
=>
{
renderInput
=
(
threshold
:
Threshold
WithKey
)
=>
{
return
(
<
div
className=
"thresholds-row-input-inner"
>
<
span
className=
"thresholds-row-input-inner-arrow"
/>
...
...
@@ -175,12 +173,11 @@ export class ThresholdsEditor extends PureComponent<Props, State> {
</
div
>
)
}
</
div
>
{
threshold
.
index
===
0
&&
(
{
!
isFinite
(
threshold
.
value
)
?
(
<
div
className=
"thresholds-row-input-inner-value"
>
<
Input
type=
"text"
value=
"Base"
readOnly
/>
</
div
>
)
}
{
threshold
.
index
>
0
&&
(
)
:
(
<>
<
div
className=
"thresholds-row-input-inner-value"
>
<
Input
...
...
@@ -189,7 +186,6 @@ export class ThresholdsEditor extends PureComponent<Props, State> {
onChange=
{
(
event
:
ChangeEvent
<
HTMLInputElement
>
)
=>
this
.
onChangeThresholdValue
(
event
,
threshold
)
}
value=
{
threshold
.
value
}
onBlur=
{
this
.
onBlur
}
readOnly=
{
threshold
.
index
===
0
}
/>
</
div
>
<
div
className=
"thresholds-row-input-inner-remove"
onClick=
{
()
=>
this
.
onRemoveThreshold
(
threshold
)
}
>
...
...
@@ -212,13 +208,10 @@ export class ThresholdsEditor extends PureComponent<Props, State> {
{
thresholds
.
slice
(
0
)
.
reverse
()
.
map
(
(
threshold
,
index
)
=>
{
.
map
(
threshold
=>
{
return
(
<
div
className=
"thresholds-row"
key=
{
`${threshold.index}-${index}`
}
>
<
div
className=
"thresholds-row-add-button"
onClick=
{
()
=>
this
.
onAddThreshold
(
threshold
.
index
+
1
)
}
>
<
div
className=
"thresholds-row"
key=
{
`${threshold.key}`
}
>
<
div
className=
"thresholds-row-add-button"
onClick=
{
()
=>
this
.
onAddThresholdAfter
(
threshold
)
}
>
<
i
className=
"fa fa-plus"
/>
</
div
>
<
div
...
...
@@ -237,3 +230,10 @@ export class ThresholdsEditor extends PureComponent<Props, State> {
);
}
}
export
function
threshodsWithoutKey
(
thresholds
:
ThresholdWithKey
[]):
Threshold
[]
{
return
thresholds
.
map
(
t
=>
{
const
{
key
,
...
rest
}
=
t
;
return
rest
;
// everything except key
});
}
packages/grafana-ui/src/components/ThresholdsEditor/__snapshots__/ThresholdsEditor.test.tsx.snap
View file @
14caa6a0
...
...
@@ -9,7 +9,6 @@ exports[`Render should render with base threshold 1`] = `
Array [
Object {
"color": "#7EB26D",
"index": 0,
"value": -Infinity,
},
],
...
...
@@ -48,7 +47,7 @@ exports[`Render should render with base threshold 1`] = `
>
<div
className="thresholds-row"
key="
0-
0"
key="
10
0"
>
<div
className="thresholds-row-add-button"
...
...
packages/grafana-ui/src/utils/displayValue.test.ts
View file @
14caa6a0
...
...
@@ -103,7 +103,7 @@ describe('Format value', () => {
it
(
'should return if value isNaN'
,
()
=>
{
const
valueMappings
:
ValueMapping
[]
=
[];
const
value
=
'N/A'
;
const
instance
=
getDisplayProcessor
({
mappings
:
valueMappings
});
const
instance
=
getDisplayProcessor
({
field
:
{
mappings
:
valueMappings
}
});
const
result
=
instance
(
value
);
...
...
@@ -114,7 +114,7 @@ describe('Format value', () => {
const
valueMappings
:
ValueMapping
[]
=
[];
const
value
=
'6'
;
const
instance
=
getDisplayProcessor
({
mappings
:
valueMappings
,
field
:
{
decimals
:
1
}
});
const
instance
=
getDisplayProcessor
({
field
:
{
decimals
:
1
,
mappings
:
valueMappings
}
});
const
result
=
instance
(
value
);
...
...
@@ -127,7 +127,7 @@ describe('Format value', () => {
{
id
:
1
,
operator
:
''
,
text
:
'1-9'
,
type
:
MappingType
.
RangeToText
,
from
:
'1'
,
to
:
'9'
},
];
const
value
=
'10'
;
const
instance
=
getDisplayProcessor
({
mappings
:
valueMappings
,
field
:
{
decimals
:
1
}
});
const
instance
=
getDisplayProcessor
({
field
:
{
decimals
:
1
,
mappings
:
valueMappings
}
});
const
result
=
instance
(
value
);
...
...
@@ -160,7 +160,7 @@ describe('Format value', () => {
{
id
:
1
,
operator
:
''
,
text
:
'elva'
,
type
:
MappingType
.
ValueToText
,
value
:
'11'
},
];
const
value
=
'11'
;
const
instance
=
getDisplayProcessor
({
mappings
:
valueMappings
,
field
:
{
decimals
:
1
}
});
const
instance
=
getDisplayProcessor
({
field
:
{
decimals
:
1
,
mappings
:
valueMappings
}
});
expect
(
instance
(
value
).
text
).
toEqual
(
'1-20'
);
});
...
...
packages/grafana-ui/src/utils/displayValue.ts
View file @
14caa6a0
...
...
@@ -7,16 +7,13 @@ import { getColorFromHexRgbOrName } from './namedColorsPalette';
// Types
import
{
DecimalInfo
,
DisplayValue
,
GrafanaTheme
,
GrafanaThemeType
,
DecimalCount
}
from
'../types'
;
import
{
DateTime
,
dateTime
,
Threshold
,
ValueMapping
,
getMappedValue
,
Field
}
from
'@grafana/data'
;
import
{
DateTime
,
dateTime
,
Threshold
,
getMappedValue
,
Field
}
from
'@grafana/data'
;
export
type
DisplayProcessor
=
(
value
:
any
)
=>
DisplayValue
;
export
interface
DisplayValueOptions
{
field
?:
Partial
<
Field
>
;
mappings
?:
ValueMapping
[];
thresholds
?:
Threshold
[];
// Alternative to empty string
noValue
?:
string
;
...
...
@@ -31,7 +28,8 @@ export function getDisplayProcessor(options?: DisplayValueOptions): DisplayProce
const
formatFunc
=
getValueFormat
(
field
.
unit
||
'none'
);
return
(
value
:
any
)
=>
{
const
{
mappings
,
thresholds
,
theme
}
=
options
;
const
{
theme
}
=
options
;
const
{
mappings
,
thresholds
}
=
field
;
let
color
;
let
text
=
_
.
toString
(
value
);
...
...
packages/grafana-ui/src/utils/fieldDisplay.test.ts
View file @
14caa6a0
import
{
getFieldProperties
,
getFieldDisplayValues
,
GetFieldDisplayValuesOptions
}
from
'./fieldDisplay'
;
import
{
FieldType
,
ReducerID
}
from
'@grafana/data'
;
import
{
FieldType
,
ReducerID
,
Threshold
}
from
'@grafana/data'
;
import
{
GrafanaThemeType
}
from
'../types/theme'
;
import
{
getTheme
}
from
'../themes/index'
;
...
...
@@ -55,8 +55,6 @@ describe('FieldDisplay', () => {
},
fieldOptions
:
{
calcs
:
[],
mappings
:
[],
thresholds
:
[],
override
:
{},
defaults
:
{},
},
...
...
@@ -68,8 +66,6 @@ describe('FieldDisplay', () => {
...
options
,
fieldOptions
:
{
calcs
:
[
ReducerID
.
first
],
mappings
:
[],
thresholds
:
[],
override
:
{},
defaults
:
{
title
:
'$__cell_0 * $__field_name * $__series_name'
,
...
...
@@ -88,8 +84,6 @@ describe('FieldDisplay', () => {
...
options
,
fieldOptions
:
{
calcs
:
[
ReducerID
.
last
],
mappings
:
[],
thresholds
:
[],
override
:
{},
defaults
:
{},
},
...
...
@@ -104,8 +98,6 @@ describe('FieldDisplay', () => {
values
:
true
,
//
limit
:
1000
,
calcs
:
[],
mappings
:
[],
thresholds
:
[],
override
:
{},
defaults
:
{},
},
...
...
@@ -120,12 +112,27 @@ describe('FieldDisplay', () => {
values
:
true
,
//
limit
:
2
,
calcs
:
[],
mappings
:
[],
thresholds
:
[],
override
:
{},
defaults
:
{},
},
});
expect
(
display
.
map
(
v
=>
v
.
display
.
numeric
)).
toEqual
([
1
,
3
]);
// First 2 are from the first field
});
it
(
'should restore -Infinity value for base threshold'
,
()
=>
{
const
field
=
getFieldProperties
({
thresholds
:
[
({
color
:
'#73BF69'
,
value
:
null
,
}
as
unknown
)
as
Threshold
,
{
color
:
'#F2495C'
,
value
:
50
,
},
],
});
expect
(
field
.
thresholds
!
.
length
).
toEqual
(
2
);
expect
(
field
.
thresholds
!
[
0
].
value
).
toBe
(
-
Infinity
);
});
});
packages/grafana-ui/src/utils/fieldDisplay.ts
View file @
14caa6a0
...
...
@@ -4,16 +4,7 @@ import toString from 'lodash/toString';
import
{
DisplayValue
,
GrafanaTheme
,
InterpolateFunction
,
ScopedVars
,
GraphSeriesValue
}
from
'../types/index'
;
import
{
getDisplayProcessor
}
from
'./displayValue'
;
import
{
getFlotPairs
}
from
'./flotPairs'
;
import
{
ValueMapping
,
Threshold
,
ReducerID
,
reduceField
,
FieldType
,
NullValueMode
,
DataFrame
,
Field
,
}
from
'@grafana/data'
;
import
{
ReducerID
,
reduceField
,
FieldType
,
NullValueMode
,
DataFrame
,
Field
}
from
'@grafana/data'
;
export
interface
FieldDisplayOptions
{
values
?:
boolean
;
// If true show each row value
...
...
@@ -22,10 +13,6 @@ export interface FieldDisplayOptions {
defaults
:
Partial
<
Field
>
;
// Use these values unless otherwise stated
override
:
Partial
<
Field
>
;
// Set these values regardless of the source
// Could these be data driven also?
thresholds
:
Threshold
[];
mappings
:
ValueMapping
[];
}
export
const
VAR_SERIES_NAME
=
'__series_name'
;
...
...
@@ -127,8 +114,6 @@ export const getFieldDisplayValues = (options: GetFieldDisplayValuesOptions): Fi
const
display
=
getDisplayProcessor
({
field
,
mappings
:
fieldOptions
.
mappings
,
thresholds
:
fieldOptions
.
thresholds
,
theme
:
options
.
theme
,
});
...
...
@@ -263,6 +248,11 @@ export function getFieldProperties(...props: PartialField[]): Field {
field
=
applyFieldProperties
(
field
,
props
[
i
]);
}
// First value is always -Infinity
if
(
field
.
thresholds
&&
field
.
thresholds
.
length
)
{
field
.
thresholds
[
0
].
value
=
-
Infinity
;
}
// Verify that max > min
if
(
field
.
hasOwnProperty
(
'min'
)
&&
field
.
hasOwnProperty
(
'max'
)
&&
field
.
min
!
>
field
.
max
!
)
{
return
{
...
...
public/app/features/dashboard/state/PanelModel.test.ts
View file @
14caa6a0
...
...
@@ -98,21 +98,6 @@ describe('PanelModel', () => {
expect
(
saveModel
.
events
).
toBe
(
undefined
);
});
it
(
'should restore -Infinity value for base threshold'
,
()
=>
{
expect
(
model
.
options
.
fieldOptions
.
thresholds
).
toEqual
([
{
color
:
'#F2495C'
,
index
:
1
,
value
:
50
,
},
{
color
:
'#73BF69'
,
index
:
0
,
value
:
-
Infinity
,
},
]);
});
describe
(
'when changing panel type'
,
()
=>
{
const
newPanelPluginDefaults
=
{
showThresholdLabels
:
false
,
...
...
@@ -180,7 +165,7 @@ describe('PanelModel', () => {
it
(
'should call react onPanelTypeChanged'
,
()
=>
{
expect
(
onPanelTypeChanged
.
mock
.
calls
.
length
).
toBe
(
1
);
expect
(
onPanelTypeChanged
.
mock
.
calls
[
0
][
1
]).
toBe
(
'table'
);
expect
(
onPanelTypeChanged
.
mock
.
calls
[
0
][
2
].
fieldOptions
.
thresholds
).
toBeDefined
();
expect
(
onPanelTypeChanged
.
mock
.
calls
[
0
][
2
].
fieldOptions
).
toBeDefined
();
});
it
(
'getQueryRunner() should return same instance after changing to another react panel'
,
()
=>
{
...
...
public/app/features/dashboard/state/PanelModel.ts
View file @
14caa6a0
...
...
@@ -136,7 +136,6 @@ export class PanelModel {
// queries must have refId
this
.
ensureQueryIds
();
this
.
restoreInfintyForThresholds
();
}
ensureQueryIds
()
{
...
...
@@ -149,16 +148,6 @@ export class PanelModel {
}
}
restoreInfintyForThresholds
()
{
if
(
this
.
options
&&
this
.
options
.
fieldOptions
)
{
for
(
const
threshold
of
this
.
options
.
fieldOptions
.
thresholds
)
{
if
(
threshold
.
value
===
null
)
{
threshold
.
value
=
-
Infinity
;
}
}
}
}
getOptions
()
{
return
this
.
options
;
}
...
...
public/app/plugins/panel/bargauge/BarGaugeMigrations.test.ts
0 → 100644
View file @
14caa6a0
import
{
PanelModel
}
from
'@grafana/ui'
;
import
{
barGaugePanelMigrationCheck
}
from
'./BarGaugeMigrations'
;
describe
(
'BarGauge Panel Migrations'
,
()
=>
{
it
(
'from 6.2'
,
()
=>
{
const
panel
=
{
id
:
7
,
links
:
[],
options
:
{
displayMode
:
'lcd'
,
fieldOptions
:
{
calcs
:
[
'mean'
],
defaults
:
{
decimals
:
null
,
max
:
-
22
,
min
:
33
,
unit
:
'watt'
,
},
mappings
:
[],
override
:
{},
thresholds
:
[
{
color
:
'green'
,
index
:
0
,
value
:
null
,
},
{
color
:
'orange'
,
index
:
1
,
value
:
40
,
},
{
color
:
'red'
,
index
:
2
,
value
:
80
,
},
],
values
:
false
,
},
orientation
:
'vertical'
,
},
pluginVersion
:
'6.2.0'
,
targets
:
[
{
refId
:
'A'
,
scenarioId
:
'random_walk'
,
},
{
refId
:
'B'
,
scenarioId
:
'random_walk'
,
},
],
timeFrom
:
null
,
timeShift
:
null
,
title
:
'Usage'
,
type
:
'bargauge'
,
}
as
PanelModel
;
expect
(
barGaugePanelMigrationCheck
(
panel
)).
toMatchSnapshot
();
});
});
public/app/plugins/panel/bargauge/BarGaugeMigrations.ts
0 → 100644
View file @
14caa6a0
import
{
PanelModel
}
from
'@grafana/ui'
;
import
{
sharedSingleStatMigrationCheck
,
migrateOldThresholds
,
}
from
'@grafana/ui/src/components/SingleStatShared/SingleStatBaseOptions'
;
import
{
BarGaugeOptions
}
from
'./types'
;
export
const
barGaugePanelMigrationCheck
=
(
panel
:
PanelModel
<
BarGaugeOptions
>
):
Partial
<
BarGaugeOptions
>
=>
{
if
(
!
panel
.
options
)
{
// This happens on the first load or when migrating from angular
return
{};
}
// Move thresholds to field
const
previousVersion
=
panel
.
pluginVersion
||
''
;
if
(
previousVersion
.
startsWith
(
'6.2'
)
||
previousVersion
.
startsWith
(
'6.3'
))
{
console
.
log
(
'TRANSFORM'
,
panel
.
options
);
const
old
=
panel
.
options
as
any
;
const
{
fieldOptions
}
=
old
;
if
(
fieldOptions
)
{
const
{
mappings
,
thresholds
,
...
rest
}
=
fieldOptions
;
rest
.
defaults
=
{
mappings
,
thresholds
:
migrateOldThresholds
(
thresholds
),
...
rest
.
defaults
,
};
return
{
...
old
,
fieldOptions
:
rest
,
};
}
}
// Default to the standard migration path
return
sharedSingleStatMigrationCheck
(
panel
);
};
public/app/plugins/panel/bargauge/BarGaugePanel.tsx
View file @
14caa6a0
...
...
@@ -14,7 +14,6 @@ import { PanelProps } from '@grafana/ui';
export
class
BarGaugePanel
extends
PureComponent
<
PanelProps
<
BarGaugeOptions
>>
{
renderValue
=
(
value
:
FieldDisplay
,
width
:
number
,
height
:
number
):
JSX
.
Element
=>
{
const
{
options
}
=
this
.
props
;
const
{
fieldOptions
}
=
options
;
const
{
field
,
display
}
=
value
;
return
(
...
...
@@ -23,7 +22,7 @@ export class BarGaugePanel extends PureComponent<PanelProps<BarGaugeOptions>> {
width=
{
width
}
height=
{
height
}
orientation=
{
options
.
orientation
}
thresholds=
{
field
Options
.
thresholds
}
thresholds=
{
field
.
thresholds
}
theme=
{
config
.
theme
}
itemSpacing=
{
this
.
getItemSpacing
()
}
displayMode=
{
options
.
displayMode
}
...
...
public/app/plugins/panel/bargauge/BarGaugePanelEditor.tsx
View file @
14caa6a0
...
...
@@ -19,17 +19,21 @@ import { Threshold, ValueMapping } from '@grafana/data';
import
{
BarGaugeOptions
,
orientationOptions
,
displayModes
}
from
'./types'
;
export
class
BarGaugePanelEditor
extends
PureComponent
<
PanelEditorProps
<
BarGaugeOptions
>>
{
onThresholdsChanged
=
(
thresholds
:
Threshold
[])
=>
this
.
onDisplayOptionsChanged
({
...
this
.
props
.
options
.
fieldOptions
,
onThresholdsChanged
=
(
thresholds
:
Threshold
[])
=>
{
const
current
=
this
.
props
.
options
.
fieldOptions
.
defaults
;
this
.
onDefaultsChange
({
...
current
,
thresholds
,
});
};
onValueMappingsChanged
=
(
mappings
:
ValueMapping
[])
=>
this
.
onDisplayOptionsChanged
({
...
this
.
props
.
options
.
fieldOptions
,
onValueMappingsChanged
=
(
mappings
:
ValueMapping
[])
=>
{
const
current
=
this
.
props
.
options
.
fieldOptions
.
defaults
;
this
.
onDefaultsChange
({
...
current
,
mappings
,
});
};
onDisplayOptionsChanged
=
(
fieldOptions
:
FieldDisplayOptions
)
=>
this
.
props
.
onOptionsChange
({
...
...
@@ -50,6 +54,7 @@ export class BarGaugePanelEditor extends PureComponent<PanelEditorProps<BarGauge
render
()
{
const
{
options
}
=
this
.
props
;
const
{
fieldOptions
}
=
options
;
const
{
defaults
}
=
fieldOptions
;
const
labelWidth
=
6
;
...
...
@@ -80,13 +85,13 @@ export class BarGaugePanelEditor extends PureComponent<PanelEditorProps<BarGauge
</
div
>
</
PanelOptionsGroup
>
<
PanelOptionsGroup
title=
"Field"
>
<
FieldPropertiesEditor
showMinMax=
{
true
}
onChange=
{
this
.
onDefaultsChange
}
value=
{
fieldOptions
.
defaults
}
/>
<
FieldPropertiesEditor
showMinMax=
{
true
}
onChange=
{
this
.
onDefaultsChange
}
value=
{
defaults
}
/>
</
PanelOptionsGroup
>
<
ThresholdsEditor
onChange=
{
this
.
onThresholdsChanged
}
thresholds=
{
fieldOption
s
.
thresholds
}
/>
<
ThresholdsEditor
onChange=
{
this
.
onThresholdsChanged
}
thresholds=
{
default
s
.
thresholds
}
/>
</
PanelOptionsGrid
>
<
ValueMappingsEditor
onChange=
{
this
.
onValueMappingsChanged
}
valueMappings=
{
fieldOption
s
.
mappings
}
/>
<
ValueMappingsEditor
onChange=
{
this
.
onValueMappingsChanged
}
valueMappings=
{
default
s
.
mappings
}
/>
</>
);
}
...
...
public/app/plugins/panel/bargauge/__snapshots__/BarGaugeMigrations.test.ts.snap
0 → 100644
View file @
14caa6a0
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`BarGauge Panel Migrations from 6.2 1`] = `
Object {
"displayMode": "lcd",
"fieldOptions": Object {
"calcs": Array [
"mean",
],
"defaults": Object {
"decimals": null,
"mappings": Array [],
"max": -22,
"min": 33,
"thresholds": Array [
Object {
"color": "green",
"value": -Infinity,
},
Object {
"color": "orange",
"value": 40,
},
Object {
"color": "red",
"value": 80,
},
],
"unit": "watt",
},
"override": Object {},
"values": false,
},
"orientation": "vertical",
}
`;
public/app/plugins/panel/bargauge/module.tsx
View file @
14caa6a0
...
...
@@ -2,8 +2,10 @@ import { PanelPlugin, sharedSingleStatOptionsCheck } from '@grafana/ui';
import
{
BarGaugePanel
}
from
'./BarGaugePanel'
;
import
{
BarGaugePanelEditor
}
from
'./BarGaugePanelEditor'
;
import
{
BarGaugeOptions
,
defaults
}
from
'./types'
;
import
{
barGaugePanelMigrationCheck
}
from
'./BarGaugeMigrations'
;
export
const
plugin
=
new
PanelPlugin
<
BarGaugeOptions
>
(
BarGaugePanel
)
.
setDefaults
(
defaults
)
.
setEditor
(
BarGaugePanelEditor
)
.
setPanelChangeHandler
(
sharedSingleStatOptionsCheck
);
.
setPanelChangeHandler
(
sharedSingleStatOptionsCheck
)
.
setMigrationHandler
(
barGaugePanelMigrationCheck
);
public/app/plugins/panel/gauge/GaugeMigrations.ts
View file @
14caa6a0
import
{
Field
,
getFieldReducers
}
from
'@grafana/data'
;
import
{
PanelModel
}
from
'@grafana/ui'
;
import
{
GaugeOptions
}
from
'./types'
;
import
{
sharedSingleStatMigrationCheck
}
from
'@grafana/ui/src/components/SingleStatShared/SingleStatBaseOptions'
;
import
{
sharedSingleStatMigrationCheck
,
migrateOldThresholds
,
}
from
'@grafana/ui/src/components/SingleStatShared/SingleStatBaseOptions'
;
import
{
FieldDisplayOptions
}
from
'@grafana/ui/src/utils/fieldDisplay'
;
export
const
gaugePanelMigrationCheck
=
(
panel
:
PanelModel
<
GaugeOptions
>
):
Partial
<
GaugeOptions
>
=>
{
...
...
@@ -10,7 +13,8 @@ export const gaugePanelMigrationCheck = (panel: PanelModel<GaugeOptions>): Parti
return
{};
}
if
(
!
panel
.
pluginVersion
||
panel
.
pluginVersion
.
startsWith
(
'6.1'
))
{
const
previousVersion
=
panel
.
pluginVersion
||
''
;
if
(
!
previousVersion
||
previousVersion
.
startsWith
(
'6.1'
))
{
const
old
=
panel
.
options
as
any
;
const
{
valueOptions
}
=
old
;
...
...
@@ -20,23 +24,36 @@ export const gaugePanelMigrationCheck = (panel: PanelModel<GaugeOptions>): Parti
options
.
orientation
=
old
.
orientation
;
const
fieldOptions
=
(
options
.
fieldOptions
=
{}
as
FieldDisplayOptions
);
fieldOptions
.
mappings
=
old
.
valueMappings
;
fieldOptions
.
thresholds
=
old
.
thresholds
;
const
field
=
(
fieldOptions
.
defaults
=
{}
as
Field
);
if
(
valueOptions
)
{
field
.
unit
=
valueOptions
.
unit
;
field
.
decimals
=
valueOptions
.
decimals
;
// Make sure the stats have a valid name
if
(
valueOptions
.
stat
)
{
fieldOptions
.
calcs
=
getFieldReducers
([
valueOptions
.
stat
]).
map
(
s
=>
s
.
id
);
}
field
.
mappings
=
old
.
valueMappings
;
field
.
thresholds
=
migrateOldThresholds
(
old
.
thresholds
)
;
field
.
unit
=
valueOptions
.
unit
;
field
.
decimals
=
valueOptions
.
decimals
;
// Make sure the stats have a valid name
if
(
valueOptions
.
stat
)
{
fieldOptions
.
calcs
=
getFieldReducers
([
valueOptions
.
stat
]).
map
(
s
=>
s
.
id
);
}
field
.
min
=
old
.
minValue
;
field
.
max
=
old
.
maxValue
;
return
options
;
}
else
if
(
previousVersion
.
startsWith
(
'6.2'
)
||
previousVersion
.
startsWith
(
'6.3'
))
{
const
old
=
panel
.
options
as
any
;
const
{
fieldOptions
}
=
old
;
if
(
fieldOptions
)
{
const
{
mappings
,
thresholds
,
...
rest
}
=
fieldOptions
;
rest
.
default
=
{
mappings
,
thresholds
:
migrateOldThresholds
(
thresholds
),
...
rest
.
defaults
,
};
return
{
...
old
.
options
,
fieldOptions
:
rest
,
};
}
}
// Default to the standard migration path
...
...
public/app/plugins/panel/gauge/GaugePanel.tsx
View file @
14caa6a0
...
...
@@ -14,7 +14,6 @@ import { PanelProps, VizRepeater } from '@grafana/ui';
export
class
GaugePanel
extends
PureComponent
<
PanelProps
<
GaugeOptions
>>
{
renderValue
=
(
value
:
FieldDisplay
,
width
:
number
,
height
:
number
):
JSX
.
Element
=>
{
const
{
options
}
=
this
.
props
;
const
{
fieldOptions
}
=
options
;
const
{
field
,
display
}
=
value
;
return
(
...
...
@@ -22,7 +21,7 @@ export class GaugePanel extends PureComponent<PanelProps<GaugeOptions>> {
value=
{
display
}
width=
{
width
}
height=
{
height
}
thresholds=
{
field
Options
.
thresholds
}
thresholds=
{
field
.
thresholds
}
showThresholdLabels=
{
options
.
showThresholdLabels
}
showThresholdMarkers=
{
options
.
showThresholdMarkers
}
minValue=
{
field
.
min
}
...
...
public/app/plugins/panel/gauge/GaugePanelEditor.tsx
View file @
14caa6a0
...
...
@@ -27,17 +27,21 @@ export class GaugePanelEditor extends PureComponent<PanelEditorProps<GaugeOption
showThresholdMarkers
:
!
this
.
props
.
options
.
showThresholdMarkers
,
});
onThresholdsChanged
=
(
thresholds
:
Threshold
[])
=>
this
.
onDisplayOptionsChanged
({
...
this
.
props
.
options
.
fieldOptions
,
onThresholdsChanged
=
(
thresholds
:
Threshold
[])
=>
{
const
current
=
this
.
props
.
options
.
fieldOptions
.
defaults
;
this
.
onDefaultsChange
({
...
current
,
thresholds
,
});
};
onValueMappingsChanged
=
(
mappings
:
ValueMapping
[])
=>
this
.
onDisplayOptionsChanged
({
...
this
.
props
.
options
.
fieldOptions
,
onValueMappingsChanged
=
(
mappings
:
ValueMapping
[])
=>
{
const
current
=
this
.
props
.
options
.
fieldOptions
.
defaults
;
this
.
onDefaultsChange
({
...
current
,
mappings
,
});
};
onDisplayOptionsChanged
=
(
fieldOptions
:
FieldDisplayOptions
)
=>
this
.
props
.
onOptionsChange
({
...
...
@@ -55,6 +59,7 @@ export class GaugePanelEditor extends PureComponent<PanelEditorProps<GaugeOption
render
()
{
const
{
options
}
=
this
.
props
;
const
{
fieldOptions
,
showThresholdLabels
,
showThresholdMarkers
}
=
options
;
const
{
defaults
}
=
fieldOptions
;
return
(
<>
...
...
@@ -80,13 +85,13 @@ export class GaugePanelEditor extends PureComponent<PanelEditorProps<GaugeOption
</
PanelOptionsGroup
>
<
PanelOptionsGroup
title=
"Field"
>
<
FieldPropertiesEditor
showMinMax=
{
true
}
onChange=
{
this
.
onDefaultsChange
}
value=
{
fieldOptions
.
defaults
}
/>
<
FieldPropertiesEditor
showMinMax=
{
true
}
onChange=
{
this
.
onDefaultsChange
}
value=
{
defaults
}
/>
</
PanelOptionsGroup
>
<
ThresholdsEditor
onChange=
{
this
.
onThresholdsChanged
}
thresholds=
{
fieldOption
s
.
thresholds
}
/>
<
ThresholdsEditor
onChange=
{
this
.
onThresholdsChanged
}
thresholds=
{
default
s
.
thresholds
}
/>
</
PanelOptionsGrid
>
<
ValueMappingsEditor
onChange=
{
this
.
onValueMappingsChanged
}
valueMappings=
{
fieldOption
s
.
mappings
}
/>
<
ValueMappingsEditor
onChange=
{
this
.
onValueMappingsChanged
}
valueMappings=
{
default
s
.
mappings
}
/>
</>
);
}
...
...
public/app/plugins/panel/gauge/__snapshots__/GaugeMigrations.test.ts.snap
View file @
14caa6a0
...
...
@@ -8,43 +8,39 @@ Object {
],
"defaults": Object {
"decimals": 3,
"mappings": Array [
Object {
"from": "50",
"id": 1,
"operator": "",
"text": "BIG",
"to": "1000",
"type": 2,
"value": "",
},
],
"max": "50",
"min": "-50",
"thresholds": Array [
Object {
"color": "green",
"value": -Infinity,
},
Object {
"color": "#EAB839",
"value": -25,
},
Object {
"color": "#6ED0E0",
"value": 0,
},
Object {
"color": "red",
"value": 25,
},
],
"unit": "accMS2",
},
"mappings": Array [
Object {
"from": "50",
"id": 1,
"operator": "",
"text": "BIG",
"to": "1000",
"type": 2,
"value": "",
},
],
"thresholds": Array [
Object {
"color": "green",
"index": 0,
"value": null,
},
Object {
"color": "#EAB839",
"index": 1,
"value": -25,
},
Object {
"color": "#6ED0E0",
"index": 2,
"value": 0,
},
Object {
"color": "red",
"index": 3,
"value": 25,
},
],
},
"orientation": "auto",
"showThresholdLabels": true,
...
...
public/app/plugins/panel/piechart/PieChartPanelEditor.tsx
View file @
14caa6a0
...
...
@@ -14,11 +14,13 @@ import { PieChartOptionsBox } from './PieChartOptionsBox';
import
{
PieChartOptions
}
from
'./types'
;
export
class
PieChartPanelEditor
extends
PureComponent
<
PanelEditorProps
<
PieChartOptions
>>
{
onValueMappingsChanged
=
(
mappings
:
ValueMapping
[])
=>
this
.
onDisplayOptionsChanged
({
...
this
.
props
.
options
.
fieldOptions
,
onValueMappingsChanged
=
(
mappings
:
ValueMapping
[])
=>
{
const
current
=
this
.
props
.
options
.
fieldOptions
.
defaults
;
this
.
onDefaultsChange
({
...
current
,
mappings
,
});
};
onDisplayOptionsChanged
=
(
fieldOptions
:
FieldDisplayOptions
)
=>
this
.
props
.
onOptionsChange
({
...
...
@@ -36,6 +38,7 @@ export class PieChartPanelEditor extends PureComponent<PanelEditorProps<PieChart
render
()
{
const
{
onOptionsChange
,
options
}
=
this
.
props
;
const
{
fieldOptions
}
=
options
;
const
{
defaults
}
=
fieldOptions
;
return
(
<>
...
...
@@ -45,13 +48,13 @@ export class PieChartPanelEditor extends PureComponent<PanelEditorProps<PieChart
</
PanelOptionsGroup
>
<
PanelOptionsGroup
title=
"Field (default)"
>
<
FieldPropertiesEditor
showMinMax=
{
true
}
onChange=
{
this
.
onDefaultsChange
}
value=
{
fieldOptions
.
defaults
}
/>
<
FieldPropertiesEditor
showMinMax=
{
true
}
onChange=
{
this
.
onDefaultsChange
}
value=
{
defaults
}
/>
</
PanelOptionsGroup
>
<
PieChartOptionsBox
onOptionsChange=
{
onOptionsChange
}
options=
{
options
}
/>
</
PanelOptionsGrid
>
<
ValueMappingsEditor
onChange=
{
this
.
onValueMappingsChanged
}
valueMappings=
{
fieldOption
s
.
mappings
}
/>
<
ValueMappingsEditor
onChange=
{
this
.
onValueMappingsChanged
}
valueMappings=
{
default
s
.
mappings
}
/>
</>
);
}
...
...
public/app/plugins/panel/singlestat2/SingleStatEditor.tsx
View file @
14caa6a0
...
...
@@ -18,17 +18,21 @@ import { FontSizeEditor } from './FontSizeEditor';
import
{
SparklineEditor
}
from
'./SparklineEditor'
;
export
class
SingleStatEditor
extends
PureComponent
<
PanelEditorProps
<
SingleStatOptions
>>
{
onThresholdsChanged
=
(
thresholds
:
Threshold
[])
=>
this
.
onDisplayOptionsChanged
({
...
this
.
props
.
options
.
fieldOptions
,
onThresholdsChanged
=
(
thresholds
:
Threshold
[])
=>
{
const
current
=
this
.
props
.
options
.
fieldOptions
.
defaults
;
this
.
onDefaultsChange
({
...
current
,
thresholds
,
});
};
onValueMappingsChanged
=
(
mappings
:
ValueMapping
[])
=>
this
.
onDisplayOptionsChanged
({
...
this
.
props
.
options
.
fieldOptions
,
onValueMappingsChanged
=
(
mappings
:
ValueMapping
[])
=>
{
const
current
=
this
.
props
.
options
.
fieldOptions
.
defaults
;
this
.
onDefaultsChange
({
...
current
,
mappings
,
});
};
onDisplayOptionsChanged
=
(
fieldOptions
:
FieldDisplayOptions
)
=>
this
.
props
.
onOptionsChange
({
...
...
@@ -52,6 +56,7 @@ export class SingleStatEditor extends PureComponent<PanelEditorProps<SingleStatO
render
()
{
const
{
options
}
=
this
.
props
;
const
{
fieldOptions
}
=
options
;
const
{
defaults
}
=
fieldOptions
;
return
(
<>
...
...
@@ -61,17 +66,17 @@ export class SingleStatEditor extends PureComponent<PanelEditorProps<SingleStatO
</
PanelOptionsGroup
>
<
PanelOptionsGroup
title=
"Field (default)"
>
<
FieldPropertiesEditor
showMinMax=
{
true
}
onChange=
{
this
.
onDefaultsChange
}
value=
{
fieldOptions
.
defaults
}
/>
<
FieldPropertiesEditor
showMinMax=
{
true
}
onChange=
{
this
.
onDefaultsChange
}
value=
{
defaults
}
/>
</
PanelOptionsGroup
>
<
FontSizeEditor
options=
{
options
}
onChange=
{
this
.
props
.
onOptionsChange
}
/>
<
ColoringEditor
options=
{
options
}
onChange=
{
this
.
props
.
onOptionsChange
}
/>
<
SparklineEditor
options=
{
options
.
sparkline
}
onChange=
{
this
.
onSparklineChanged
}
/>
<
ThresholdsEditor
onChange=
{
this
.
onThresholdsChanged
}
thresholds=
{
fieldOption
s
.
thresholds
}
/>
<
ThresholdsEditor
onChange=
{
this
.
onThresholdsChanged
}
thresholds=
{
default
s
.
thresholds
}
/>
</
PanelOptionsGrid
>
<
ValueMappingsEditor
onChange=
{
this
.
onValueMappingsChanged
}
valueMappings=
{
fieldOption
s
.
mappings
}
/>
<
ValueMappingsEditor
onChange=
{
this
.
onValueMappingsChanged
}
valueMappings=
{
default
s
.
mappings
}
/>
</>
);
}
...
...
public/app/plugins/panel/singlestat2/types.ts
View file @
14caa6a0
...
...
@@ -25,10 +25,15 @@ export interface SingleStatOptions extends SingleStatBaseOptions {
export
const
standardFieldDisplayOptions
:
FieldDisplayOptions
=
{
values
:
false
,
calcs
:
[
ReducerID
.
mean
],
defaults
:
{},
defaults
:
{
min
:
0
,
max
:
100
,
thresholds
:
[
{
value
:
-
Infinity
,
color
:
'green'
},
{
value
:
80
,
color
:
'red'
},
// 80%
],
},
override
:
{},
mappings
:
[],
thresholds
:
[{
index
:
0
,
value
:
-
Infinity
,
color
:
'green'
},
{
index
:
1
,
value
:
80
,
color
:
'red'
}],
};
export
const
defaults
:
SingleStatOptions
=
{
...
...
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