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
8250b59d
Unverified
Commit
8250b59d
authored
Dec 16, 2020
by
Ryan McKinley
Committed by
GitHub
Dec 16, 2020
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
GraphNG: support fill gradient (#29765)
parent
0c849840
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
11 changed files
with
247 additions
and
38 deletions
+247
-38
devenv/dev-dashboards/panel-graph/graph-ng-gradient-area.json
+0
-0
packages/grafana-ui/src/components/GraphNG/GraphNG.tsx
+1
-0
packages/grafana-ui/src/components/Sparkline/Sparkline.tsx
+1
-0
packages/grafana-ui/src/components/uPlot/Plot.tsx
+6
-3
packages/grafana-ui/src/components/uPlot/config.ts
+43
-1
packages/grafana-ui/src/components/uPlot/config/UPlotConfigBuilder.test.ts
+52
-3
packages/grafana-ui/src/components/uPlot/config/UPlotSeriesBuilder.ts
+85
-21
packages/grafana-ui/src/utils/measureText.ts
+17
-8
public/app/plugins/panel/graph3/__snapshots__/migrations.test.ts.snap
+2
-1
public/app/plugins/panel/graph3/migrations.ts
+31
-1
public/app/plugins/panel/graph3/module.tsx
+9
-0
No files found.
devenv/dev-dashboards/panel-graph/graph-ng-gradient-area.json
0 → 100644
View file @
8250b59d
This diff is collapsed.
Click to expand it.
packages/grafana-ui/src/components/GraphNG/GraphNG.tsx
View file @
8250b59d
...
...
@@ -165,6 +165,7 @@ export const GraphNG: React.FC<GraphNGProps> = ({
pointColor
:
customConfig
.
pointColor
??
seriesColor
,
fillOpacity
:
customConfig
.
fillOpacity
,
spanNulls
:
customConfig
.
spanNulls
||
false
,
fillGradient
:
customConfig
.
fillGradient
,
});
if
(
hasLegend
.
current
)
{
...
...
packages/grafana-ui/src/components/Sparkline/Sparkline.tsx
View file @
8250b59d
...
...
@@ -133,6 +133,7 @@ export class Sparkline extends PureComponent<Props, State> {
const
colorMode
=
getFieldColorModeForField
(
field
);
const
seriesColor
=
colorMode
.
getCalculator
(
field
,
theme
)(
0
,
0
);
const
pointsMode
=
customConfig
.
drawStyle
===
DrawStyle
.
Points
?
PointVisibility
.
Always
:
customConfig
.
showPoints
;
builder
.
addSeries
({
scaleKey
,
drawStyle
:
customConfig
.
drawStyle
!
,
...
...
packages/grafana-ui/src/components/uPlot/Plot.tsx
View file @
8250b59d
...
...
@@ -8,9 +8,12 @@ import { DataFrame } from '@grafana/data';
import
{
UPlotConfigBuilder
}
from
'./config/UPlotConfigBuilder'
;
import
usePrevious
from
'react-use/lib/usePrevious'
;
// uPlot abstraction responsible for plot initialisation, setup and refresh
// Receives a data frame that is x-axis aligned, as of https://github.com/leeoniya/uPlot/tree/master/docs#data-format
// Exposes contexts for plugins registration and uPlot instance access
/**
* @internal
* uPlot abstraction responsible for plot initialisation, setup and refresh
* Receives a data frame that is x-axis aligned, as of https://github.com/leeoniya/uPlot/tree/master/docs#data-format
* Exposes contexts for plugins registration and uPlot instance access
*/
export
const
UPlotChart
:
React
.
FC
<
PlotProps
>
=
props
=>
{
const
canvasRef
=
useRef
<
HTMLDivElement
>
(
null
);
const
plotInstance
=
useRef
<
uPlot
>
();
...
...
packages/grafana-ui/src/components/uPlot/config.ts
View file @
8250b59d
import
{
SelectableValue
}
from
'@grafana/data'
;
/**
* @alpha
*/
export
enum
AxisPlacement
{
Auto
=
'auto'
,
// First axis on the left, the rest on the right
Top
=
'top'
,
...
...
@@ -9,18 +12,27 @@ export enum AxisPlacement {
Hidden
=
'hidden'
,
}
/**
* @alpha
*/
export
enum
PointVisibility
{
Auto
=
'auto'
,
// will show points when the density is low or line is hidden
Never
=
'never'
,
Always
=
'always'
,
}
/**
* @alpha
*/
export
enum
DrawStyle
{
Line
=
'line'
,
// default
Bars
=
'bars'
,
// will also have a gap percent
Points
=
'points'
,
// Only show points
}
/**
* @alpha
*/
export
enum
LineInterpolation
{
Linear
=
'linear'
,
Smooth
=
'smooth'
,
...
...
@@ -28,6 +40,9 @@ export enum LineInterpolation {
StepAfter
=
'stepAfter'
,
}
/**
* @alpha
*/
export
enum
ScaleDistribution
{
Linear
=
'linear'
,
Logarithmic
=
'log'
,
...
...
@@ -40,6 +55,7 @@ export interface LineConfig {
lineColor
?:
string
;
lineWidth
?:
number
;
lineInterpolation
?:
LineInterpolation
;
lineDash
?:
number
[];
spanNulls
?:
boolean
;
}
...
...
@@ -49,6 +65,16 @@ export interface LineConfig {
export
interface
AreaConfig
{
fillColor
?:
string
;
fillOpacity
?:
number
;
fillGradient
?:
AreaGradientMode
;
}
/**
* @alpha
*/
export
enum
AreaGradientMode
{
None
=
'none'
,
Opacity
=
'opacity'
,
Hue
=
'hue'
,
}
/**
...
...
@@ -61,11 +87,18 @@ export interface PointsConfig {
pointSymbol
?:
string
;
// eventually dot,star, etc
}
/**
* @alpha
*/
export
interface
ScaleDistributionConfig
{
type
:
ScaleDistribution
;
log
?:
number
;
}
// Axis is actually unique based on the unit... not each field!
/**
* @alpha
* Axis is actually unique based on the unit... not each field!
*/
export
interface
AxisConfig
{
axisPlacement
?:
AxisPlacement
;
axisLabel
?:
string
;
...
...
@@ -80,6 +113,9 @@ export interface GraphFieldConfig extends LineConfig, AreaConfig, PointsConfig,
drawStyle
?:
DrawStyle
;
}
/**
* @alpha
*/
export
const
graphFieldOptions
=
{
drawStyle
:
[
{
label
:
'Lines'
,
value
:
DrawStyle
.
Line
},
...
...
@@ -106,4 +142,10 @@ export const graphFieldOptions = {
{
label
:
'Right'
,
value
:
AxisPlacement
.
Right
},
{
label
:
'Hidden'
,
value
:
AxisPlacement
.
Hidden
},
]
as
Array
<
SelectableValue
<
AxisPlacement
>>
,
fillGradient
:
[
{
label
:
'None'
,
value
:
undefined
},
{
label
:
'Opacity'
,
value
:
AreaGradientMode
.
Opacity
},
{
label
:
'Hue'
,
value
:
AreaGradientMode
.
Hue
},
]
as
Array
<
SelectableValue
<
AreaGradientMode
>>
,
};
packages/grafana-ui/src/components/uPlot/config/UPlotConfigBuilder.test.ts
View file @
8250b59d
...
...
@@ -3,7 +3,7 @@
import
{
UPlotConfigBuilder
}
from
'./UPlotConfigBuilder'
;
import
{
GrafanaTheme
}
from
'@grafana/data'
;
import
{
expect
}
from
'../../../../../../public/test/lib/common'
;
import
{
AxisPlacement
,
DrawStyle
,
PointVisibility
,
ScaleDistribution
}
from
'../config'
;
import
{
A
reaGradientMode
,
A
xisPlacement
,
DrawStyle
,
PointVisibility
,
ScaleDistribution
}
from
'../config'
;
describe
(
'UPlotConfigBuilder'
,
()
=>
{
describe
(
'scales config'
,
()
=>
{
...
...
@@ -266,13 +266,62 @@ describe('UPlotConfigBuilder', () => {
expect
(
builder
.
getAxisPlacement
(
'y2'
)).
toBe
(
AxisPlacement
.
Right
);
});
it
(
'When fillColor is not set fill'
,
()
=>
{
const
builder
=
new
UPlotConfigBuilder
();
builder
.
addSeries
({
drawStyle
:
DrawStyle
.
Line
,
scaleKey
:
'scale-x'
,
lineColor
:
'#0000ff'
,
});
expect
(
builder
.
getConfig
().
series
[
1
].
fill
).
toBe
(
undefined
);
});
it
(
'When fillOpacity is set'
,
()
=>
{
const
builder
=
new
UPlotConfigBuilder
();
builder
.
addSeries
({
drawStyle
:
DrawStyle
.
Line
,
scaleKey
:
'scale-x'
,
lineColor
:
'#FFAABB'
,
fillOpacity
:
50
,
});
expect
(
builder
.
getConfig
().
series
[
1
].
fill
).
toBe
(
'rgba(255, 170, 187, 0.5)'
);
});
it
(
'When fillColor is set ignore fillOpacity'
,
()
=>
{
const
builder
=
new
UPlotConfigBuilder
();
builder
.
addSeries
({
drawStyle
:
DrawStyle
.
Line
,
scaleKey
:
'scale-x'
,
lineColor
:
'#FFAABB'
,
fillOpacity
:
50
,
fillColor
:
'#FF0000'
,
});
expect
(
builder
.
getConfig
().
series
[
1
].
fill
).
toBe
(
'#FF0000'
);
});
it
(
'When fillGradient mode is opacity'
,
()
=>
{
const
builder
=
new
UPlotConfigBuilder
();
builder
.
addSeries
({
drawStyle
:
DrawStyle
.
Line
,
scaleKey
:
'scale-x'
,
lineColor
:
'#FFAABB'
,
fillOpacity
:
50
,
fillGradient
:
AreaGradientMode
.
Opacity
,
});
expect
(
builder
.
getConfig
().
series
[
1
].
fill
).
toBeInstanceOf
(
Function
);
});
it
(
'allows series configuration'
,
()
=>
{
const
builder
=
new
UPlotConfigBuilder
();
builder
.
addSeries
({
drawStyle
:
DrawStyle
.
Line
,
scaleKey
:
'scale-x'
,
fillColor
:
'#ff0000'
,
fillOpacity
:
50
,
fillGradient
:
AreaGradientMode
.
Opacity
,
showPoints
:
PointVisibility
.
Auto
,
pointSize
:
5
,
pointColor
:
'#00ff00'
,
...
...
@@ -299,7 +348,7 @@ describe('UPlotConfigBuilder', () => {
"series": Array [
Object {},
Object {
"fill":
"rgba(255, 0, 0, 0.5)"
,
"fill":
[Function]
,
"paths": [Function],
"points": Object {
"fill": "#00ff00",
...
...
packages/grafana-ui/src/components/uPlot/config/UPlotSeriesBuilder.ts
View file @
8250b59d
import
tinycolor
from
'tinycolor2'
;
import
uPlot
,
{
Series
}
from
'uplot'
;
import
{
DrawStyle
,
LineConfig
,
AreaConfig
,
PointsConfig
,
PointVisibility
,
LineInterpolation
}
from
'../config'
;
import
{
getCanvasContext
}
from
'../../../utils/measureText'
;
import
{
DrawStyle
,
LineConfig
,
AreaConfig
,
PointsConfig
,
PointVisibility
,
LineInterpolation
,
AreaGradientMode
,
}
from
'../config'
;
import
{
PlotConfigBuilder
}
from
'../types'
;
export
interface
SeriesProps
extends
LineConfig
,
AreaConfig
,
PointsConfig
{
...
...
@@ -18,8 +27,6 @@ export class UPlotSeriesBuilder extends PlotConfigBuilder<SeriesProps, Series> {
showPoints
,
pointColor
,
pointSize
,
fillColor
,
fillOpacity
,
scaleKey
,
spanNulls
,
}
=
this
.
props
;
...
...
@@ -56,31 +63,41 @@ export class UPlotSeriesBuilder extends PlotConfigBuilder<SeriesProps, Series> {
pointsConfig
.
points
!
.
show
=
true
;
}
let
fillConfig
:
any
|
undefined
;
let
fillOpacityNumber
=
fillOpacity
??
0
;
if
(
fillColor
)
{
fillConfig
=
{
fill
:
fillColor
,
};
}
if
(
fillOpacityNumber
!==
0
)
{
fillConfig
=
{
fill
:
tinycolor
(
fillColor
??
lineColor
)
.
setAlpha
(
fillOpacityNumber
/
100
)
.
toRgbString
(),
};
}
return
{
scale
:
scaleKey
,
spanGaps
:
spanNulls
,
fill
:
this
.
getFill
(),
...
lineConfig
,
...
pointsConfig
,
...
fillConfig
,
};
}
getFill
():
Series
.
Fill
|
undefined
{
const
{
lineColor
,
fillColor
,
fillGradient
,
fillOpacity
}
=
this
.
props
;
if
(
fillColor
)
{
return
fillColor
;
}
const
mode
=
fillGradient
??
AreaGradientMode
.
None
;
let
fillOpacityNumber
=
fillOpacity
??
0
;
if
(
mode
!==
AreaGradientMode
.
None
)
{
return
getCanvasGradient
({
color
:
(
fillColor
??
lineColor
)
!
,
opacity
:
fillOpacityNumber
/
100
,
mode
,
});
}
if
(
fillOpacityNumber
>
0
)
{
return
tinycolor
(
lineColor
)
.
setAlpha
(
fillOpacityNumber
/
100
)
.
toString
();
}
return
undefined
;
}
}
interface
PathBuilders
{
...
...
@@ -130,3 +147,50 @@ function mapDrawStyleToPathBuilder(
return
builders
.
linear
;
// the default
}
interface
AreaGradientOptions
{
color
:
string
;
mode
:
AreaGradientMode
;
opacity
:
number
;
}
function
getCanvasGradient
(
opts
:
AreaGradientOptions
):
(
self
:
uPlot
,
seriesIdx
:
number
)
=>
CanvasGradient
{
return
(
plot
:
uPlot
,
seriesIdx
:
number
)
=>
{
const
{
color
,
mode
,
opacity
}
=
opts
;
const
ctx
=
getCanvasContext
();
const
gradient
=
ctx
.
createLinearGradient
(
0
,
plot
.
bbox
.
top
,
0
,
plot
.
bbox
.
top
+
plot
.
bbox
.
height
);
switch
(
mode
)
{
case
AreaGradientMode
.
Hue
:
const
color1
=
tinycolor
(
color
)
.
spin
(
-
25
)
.
darken
(
30
)
.
setAlpha
(
opacity
)
.
toRgbString
();
const
color2
=
tinycolor
(
color
)
.
spin
(
25
)
.
lighten
(
35
)
.
setAlpha
(
opacity
)
.
toRgbString
();
gradient
.
addColorStop
(
0
,
color2
);
gradient
.
addColorStop
(
1
,
color1
);
case
AreaGradientMode
.
Opacity
:
default
:
gradient
.
addColorStop
(
0
,
tinycolor
(
color
)
.
setAlpha
(
opacity
)
.
toRgbString
()
);
gradient
.
addColorStop
(
1
,
tinycolor
(
color
)
.
setAlpha
(
0
)
.
toRgbString
()
);
return
gradient
;
}
};
}
packages/grafana-ui/src/utils/measureText.ts
View file @
8250b59d
...
...
@@ -2,6 +2,22 @@ let canvas: HTMLCanvasElement | null = null;
const
cache
:
Record
<
string
,
TextMetrics
>
=
{};
/**
* @internal
*/
export
function
getCanvasContext
()
{
if
(
canvas
===
null
)
{
canvas
=
document
.
createElement
(
'canvas'
);
}
const
context
=
canvas
.
getContext
(
'2d'
);
if
(
!
context
)
{
throw
new
Error
(
'Could not create context'
);
}
return
context
;
}
/**
* @beta
*/
export
function
measureText
(
text
:
string
,
fontSize
:
number
):
TextMetrics
{
...
...
@@ -13,14 +29,7 @@ export function measureText(text: string, fontSize: number): TextMetrics {
return
fromCache
;
}
if
(
canvas
===
null
)
{
canvas
=
document
.
createElement
(
'canvas'
);
}
const
context
=
canvas
.
getContext
(
'2d'
);
if
(
!
context
)
{
throw
new
Error
(
'Could not create context'
);
}
const
context
=
getCanvasContext
();
context
.
font
=
fontStyle
;
const
metrics
=
context
.
measureText
(
text
);
...
...
public/app/plugins/panel/graph3/__snapshots__/migrations.test.ts.snap
View file @
8250b59d
...
...
@@ -33,7 +33,8 @@ Object {
"custom": Object {
"axisPlacement": "hidden",
"drawStyle": "line",
"fillOpacity": 50,
"fillGradient": "opacity",
"fillOpacity": 60,
"lineInterpolation": "stepAfter",
"lineWidth": 1,
"pointSize": 6,
...
...
public/app/plugins/panel/graph3/migrations.ts
View file @
8250b59d
...
...
@@ -11,7 +11,13 @@ import {
FieldColorModeId
,
}
from
'@grafana/data'
;
import
{
GraphFieldConfig
,
LegendDisplayMode
}
from
'@grafana/ui'
;
import
{
AxisPlacement
,
DrawStyle
,
LineInterpolation
,
PointVisibility
}
from
'@grafana/ui/src/components/uPlot/config'
;
import
{
AreaGradientMode
,
AxisPlacement
,
DrawStyle
,
LineInterpolation
,
PointVisibility
,
}
from
'@grafana/ui/src/components/uPlot/config'
;
import
{
Options
}
from
'./types'
;
import
omitBy
from
'lodash/omitBy'
;
import
isNil
from
'lodash/isNil'
;
...
...
@@ -112,6 +118,18 @@ export function flotToGraphOptions(angular: any): { fieldConfig: FieldConfigSour
value
:
v
*
10
,
// was 0-10, new graph is 0 - 100
});
break
;
case
'fillGradient'
:
if
(
v
)
{
rule
.
properties
.
push
({
id
:
'custom.fillGradient'
,
value
:
'opacity'
,
// was 0-10
});
rule
.
properties
.
push
({
id
:
'custom.fillOpacity'
,
value
:
v
*
10
,
// was 0-10, new graph is 0 - 100
});
}
break
;
case
'points'
:
rule
.
properties
.
push
({
id
:
'custom.showPoints'
,
...
...
@@ -159,6 +177,7 @@ export function flotToGraphOptions(angular: any): { fieldConfig: FieldConfigSour
const
graph
=
y1
.
custom
??
({}
as
GraphFieldConfig
);
graph
.
drawStyle
=
angular
.
bars
?
DrawStyle
.
Bars
:
angular
.
lines
?
DrawStyle
.
Line
:
DrawStyle
.
Points
;
if
(
angular
.
points
)
{
graph
.
showPoints
=
PointVisibility
.
Always
;
}
else
if
(
graph
.
drawStyle
!==
DrawStyle
.
Points
)
{
...
...
@@ -166,19 +185,30 @@ export function flotToGraphOptions(angular: any): { fieldConfig: FieldConfigSour
}
graph
.
lineWidth
=
angular
.
linewidth
;
if
(
isNumber
(
angular
.
pointradius
))
{
graph
.
pointSize
=
2
+
angular
.
pointradius
*
2
;
}
if
(
isNumber
(
angular
.
fill
))
{
graph
.
fillOpacity
=
angular
.
fill
*
10
;
// fill was 0 - 10, new is 0 to 100
}
if
(
isNumber
(
angular
.
fillGradient
)
&&
angular
.
fillGradient
>
0
)
{
graph
.
fillGradient
=
AreaGradientMode
.
Opacity
;
graph
.
fillOpacity
=
angular
.
fillGradient
*
10
;
// fill is 0-10
}
graph
.
spanNulls
=
angular
.
nullPointMode
===
NullValueMode
.
Null
;
if
(
angular
.
steppedLine
)
{
graph
.
lineInterpolation
=
LineInterpolation
.
StepAfter
;
}
if
(
graph
.
drawStyle
===
DrawStyle
.
Bars
)
{
graph
.
fillOpacity
=
1.0
;
// bars were always
}
y1
.
custom
=
omitBy
(
graph
,
isNil
);
y1
.
nullValueMode
=
angular
.
nullPointMode
as
NullValueMode
;
...
...
public/app/plugins/panel/graph3/module.tsx
View file @
8250b59d
...
...
@@ -75,6 +75,15 @@ export const plugin = new PanelPlugin<Options, GraphFieldConfig>(GraphPanel)
showIf
:
c
=>
c
.
drawStyle
!==
DrawStyle
.
Points
,
})
.
addRadio
({
path
:
'fillGradient'
,
name
:
'Fill gradient'
,
defaultValue
:
graphFieldOptions
.
fillGradient
[
0
],
settings
:
{
options
:
graphFieldOptions
.
fillGradient
,
},
showIf
:
c
=>
!!
(
c
.
drawStyle
!==
DrawStyle
.
Points
&&
c
.
fillOpacity
&&
c
.
fillOpacity
>
0
),
})
.
addRadio
({
path
:
'spanNulls'
,
name
:
'Null values'
,
defaultValue
:
false
,
...
...
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