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
34f9b3ff
Unverified
Commit
34f9b3ff
authored
May 14, 2019
by
Dominik Prokop
Committed by
GitHub
May 14, 2019
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Explore: use @grafana/ui legend (#17027)
parent
13f137a1
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
9 changed files
with
145 additions
and
127 deletions
+145
-127
packages/grafana-ui/src/components/Graph/GraphLegend.tsx
+5
-3
packages/grafana-ui/src/components/Graph/GraphLegendItem.tsx
+28
-7
packages/grafana-ui/src/components/Legend/LegendList.tsx
+1
-1
packages/grafana-ui/src/components/Legend/LegendSeriesIcon.tsx
+29
-3
packages/grafana-ui/src/components/Legend/SeriesIcon.tsx
+7
-2
packages/grafana-ui/src/components/index.ts
+11
-1
public/app/features/explore/Graph.tsx
+64
-44
public/app/features/explore/Legend.tsx
+0
-66
public/app/features/explore/__snapshots__/Graph.test.tsx.snap
+0
-0
No files found.
packages/grafana-ui/src/components/Graph/GraphLegend.tsx
View file @
34f9b3ff
...
...
@@ -14,10 +14,10 @@ interface GraphLegendProps extends LegendProps {
displayMode
:
LegendDisplayMode
;
sortBy
?:
string
;
sortDesc
?:
boolean
;
onSeriesColorChange
:
SeriesColorChangeHandler
;
onSeriesColorChange
?
:
SeriesColorChangeHandler
;
onSeriesAxisToggle
?:
SeriesAxisToggleHandler
;
onToggleSort
:
(
sortBy
:
string
)
=>
void
;
onLabelClick
:
(
item
:
LegendItem
,
event
:
React
.
MouseEvent
<
HTMLElement
>
)
=>
void
;
onToggleSort
?
:
(
sortBy
:
string
)
=>
void
;
onLabelClick
?
:
(
item
:
LegendItem
,
event
:
React
.
MouseEvent
<
HTMLElement
>
)
=>
void
;
}
export
const
GraphLegend
:
React
.
FunctionComponent
<
GraphLegendProps
>
=
({
...
...
@@ -116,3 +116,5 @@ export const GraphLegend: React.FunctionComponent<GraphLegendProps> = ({
/>
);
};
GraphLegend
.
displayName
=
'GraphLegend'
;
packages/grafana-ui/src/components/Graph/GraphLegendItem.tsx
View file @
34f9b3ff
...
...
@@ -10,9 +10,9 @@ export interface GraphLegendItemProps {
key
?:
React
.
Key
;
item
:
LegendItem
;
className
?:
string
;
onLabelClick
:
(
item
:
LegendItem
,
event
:
React
.
MouseEvent
<
HTMLDivElement
>
)
=>
void
;
onSeriesColorChange
:
SeriesColorChangeHandler
;
onToggleAxis
:
()
=>
void
;
onLabelClick
?
:
(
item
:
LegendItem
,
event
:
React
.
MouseEvent
<
HTMLDivElement
>
)
=>
void
;
onSeriesColorChange
?
:
SeriesColorChangeHandler
;
onToggleAxis
?
:
()
=>
void
;
}
export
const
GraphLegendListItem
:
React
.
FunctionComponent
<
GraphLegendItemProps
>
=
({
...
...
@@ -21,19 +21,31 @@ export const GraphLegendListItem: React.FunctionComponent<GraphLegendItemProps>
onToggleAxis
,
onLabelClick
,
})
=>
{
const
theme
=
useContext
(
ThemeContext
);
return
(
<>
<
LegendSeriesIcon
disabled=
{
!
onSeriesColorChange
}
color=
{
item
.
color
}
onColorChange=
{
color
=>
onSeriesColorChange
(
item
.
label
,
color
)
}
onColorChange=
{
color
=>
{
if
(
onSeriesColorChange
)
{
onSeriesColorChange
(
item
.
label
,
color
);
}
}
}
onToggleAxis=
{
onToggleAxis
}
yAxis=
{
item
.
yAxis
}
/>
<
div
onClick=
{
event
=>
onLabelClick
(
item
,
event
)
}
onClick=
{
event
=>
{
if
(
onLabelClick
)
{
onLabelClick
(
item
,
event
);
}
}
}
className=
{
css
`
cursor: pointer;
white-space: nowrap;
color: ${!item.isVisible && theme.colors.linkDisabled};
`
}
>
{
item
.
label
}
...
...
@@ -74,13 +86,22 @@ export const GraphLegendTableRow: React.FunctionComponent<GraphLegendItemProps>
`
}
>
<
LegendSeriesIcon
disabled=
{
!!
onSeriesColorChange
}
color=
{
item
.
color
}
onColorChange=
{
color
=>
onSeriesColorChange
(
item
.
label
,
color
)
}
onColorChange=
{
color
=>
{
if
(
onSeriesColorChange
)
{
onSeriesColorChange
(
item
.
label
,
color
);
}
}
}
onToggleAxis=
{
onToggleAxis
}
yAxis=
{
item
.
yAxis
}
/>
<
div
onClick=
{
event
=>
onLabelClick
(
item
,
event
)
}
onClick=
{
event
=>
{
if
(
onLabelClick
)
{
onLabelClick
(
item
,
event
);
}
}
}
className=
{
css
`
cursor: pointer;
white-space: nowrap;
...
...
packages/grafana-ui/src/components/Legend/LegendList.tsx
View file @
34f9b3ff
...
...
@@ -28,7 +28,7 @@ export const LegendList: React.FunctionComponent<LegendComponentProps> = ({
);
};
const
getItemKey
=
(
item
:
LegendItem
)
=>
item
.
label
;
const
getItemKey
=
(
item
:
LegendItem
)
=>
`
${
item
.
label
}
`
;
const
styles
=
{
wrapper
:
cx
(
...
...
packages/grafana-ui/src/components/Legend/LegendSeriesIcon.tsx
View file @
34f9b3ff
import
React
from
'react'
;
import
{
css
,
cx
}
from
'emotion'
;
import
{
SeriesColorPicker
}
from
'../ColorPicker/ColorPicker'
;
import
{
SeriesIcon
}
from
'./SeriesIcon'
;
import
{
SeriesIcon
,
SeriesIconProps
}
from
'./SeriesIcon'
;
interface
LegendSeriesIconProps
{
disabled
:
boolean
;
color
:
string
;
yAxis
:
number
;
onColorChange
:
(
color
:
string
)
=>
void
;
...
...
@@ -10,12 +12,36 @@ interface LegendSeriesIconProps {
}
export
const
LegendSeriesIcon
:
React
.
FunctionComponent
<
LegendSeriesIconProps
>
=
({
disabled
,
yAxis
,
color
,
onColorChange
,
onToggleAxis
,
})
=>
{
return
(
let
iconProps
:
SeriesIconProps
=
{
color
,
};
if
(
!
disabled
)
{
iconProps
=
{
...
iconProps
,
className
:
'pointer'
,
};
}
return
disabled
?
(
<
span
className=
{
cx
(
'graph-legend-icon'
,
disabled
&&
css
`
cursor: default;
`
)
}
>
<
SeriesIcon
{
...
iconProps
}
/>
</
span
>
)
:
(
<
SeriesColorPicker
yaxis=
{
yAxis
}
color=
{
color
}
...
...
@@ -25,7 +51,7 @@ export const LegendSeriesIcon: React.FunctionComponent<LegendSeriesIconProps> =
>
{
({
ref
,
showColorPicker
,
hideColorPicker
})
=>
(
<
span
ref=
{
ref
}
onClick=
{
showColorPicker
}
onMouseLeave=
{
hideColorPicker
}
className=
"graph-legend-icon"
>
<
SeriesIcon
color=
{
color
}
/>
<
SeriesIcon
{
...
iconProps
}
/>
</
span
>
)
}
</
SeriesColorPicker
>
...
...
packages/grafana-ui/src/components/Legend/SeriesIcon.tsx
View file @
34f9b3ff
import
React
from
'react'
;
import
{
cx
}
from
'emotion'
;
export
const
SeriesIcon
:
React
.
FunctionComponent
<
{
color
:
string
}
>
=
({
color
})
=>
{
return
<
i
className=
"fa fa-minus pointer"
style=
{
{
color
}
}
/>;
export
interface
SeriesIconProps
{
color
:
string
;
className
?:
string
;
}
export
const
SeriesIcon
:
React
.
FunctionComponent
<
SeriesIconProps
>
=
({
color
,
className
})
=>
{
return
<
i
className=
{
cx
(
'fa'
,
'fa-minus'
,
className
)
}
style=
{
{
color
}
}
/>;
};
packages/grafana-ui/src/components/index.ts
View file @
34f9b3ff
...
...
@@ -45,10 +45,20 @@ export { TableInputCSV } from './Table/TableInputCSV';
export
{
BigValue
}
from
'./BigValue/BigValue'
;
export
{
Gauge
}
from
'./Gauge/Gauge'
;
export
{
Graph
}
from
'./Graph/Graph'
;
export
{
GraphLegend
}
from
'./Graph/GraphLegend'
;
export
{
GraphWithLegend
}
from
'./Graph/GraphWithLegend'
;
export
{
BarGauge
}
from
'./BarGauge/BarGauge'
;
export
{
VizRepeater
}
from
'./VizRepeater/VizRepeater'
;
export
{
LegendOptions
,
LegendBasicOptions
,
LegendRenderOptions
,
LegendList
,
LegendTable
}
from
'./Legend/Legend'
;
export
{
LegendOptions
,
LegendBasicOptions
,
LegendRenderOptions
,
LegendList
,
LegendTable
,
LegendItem
,
LegendPlacement
,
LegendDisplayMode
,
}
from
'./Legend/Legend'
;
// Panel editors
export
{
ThresholdsEditor
}
from
'./ThresholdsEditor/ThresholdsEditor'
;
export
{
ClickOutsideWrapper
}
from
'./ClickOutsideWrapper/ClickOutsideWrapper'
;
...
...
public/app/features/explore/Graph.tsx
View file @
34f9b3ff
import
$
from
'jquery'
;
import
React
,
{
PureComponent
}
from
'react'
;
import
difference
from
'lodash/difference'
;
import
'vendor/flot/jquery.flot'
;
import
'vendor/flot/jquery.flot.time'
;
import
'vendor/flot/jquery.flot.selection'
;
import
'vendor/flot/jquery.flot.stack'
;
import
{
TimeZone
,
AbsoluteTimeRange
}
from
'@grafana/ui'
;
import
{
TimeZone
,
AbsoluteTimeRange
,
GraphLegend
,
LegendItem
,
LegendDisplayMode
}
from
'@grafana/ui'
;
import
TimeSeries
from
'app/core/time_series2'
;
import
Legend
from
'./Legend'
;
import
{
equal
,
intersect
}
from
'./utils/set'
;
const
MAX_NUMBER_OF_TIME_SERIES
=
20
;
// Copied from graph.ts
...
...
@@ -89,7 +87,7 @@ interface GraphState {
* Type parameter refers to the `alias` property of a `TimeSeries`.
* Consequently, all series sharing the same alias will share visibility state.
*/
hiddenSeries
:
Set
<
string
>
;
hiddenSeries
:
string
[]
;
showAllTimeSeries
:
boolean
;
}
...
...
@@ -98,11 +96,11 @@ export class Graph extends PureComponent<GraphProps, GraphState> {
dynamicOptions
=
null
;
state
=
{
hiddenSeries
:
new
Set
()
,
hiddenSeries
:
[]
,
showAllTimeSeries
:
false
,
};
getGraphData
()
{
getGraphData
()
:
TimeSeries
[]
{
const
{
data
}
=
this
.
props
;
return
this
.
state
.
showAllTimeSeries
?
data
:
data
.
slice
(
0
,
MAX_NUMBER_OF_TIME_SERIES
);
...
...
@@ -121,7 +119,7 @@ export class Graph extends PureComponent<GraphProps, GraphState> {
prevProps
.
split
!==
this
.
props
.
split
||
prevProps
.
height
!==
this
.
props
.
height
||
prevProps
.
width
!==
this
.
props
.
width
||
!
equal
(
prevState
.
hiddenSeries
,
this
.
state
.
hiddenSeries
)
prevState
.
hiddenSeries
!==
this
.
state
.
hiddenSeries
)
{
this
.
draw
();
}
...
...
@@ -168,38 +166,6 @@ export class Graph extends PureComponent<GraphProps, GraphState> {
);
};
onToggleSeries
=
(
series
:
TimeSeries
,
exclusive
:
boolean
)
=>
{
this
.
setState
((
state
,
props
)
=>
{
const
{
data
,
onToggleSeries
}
=
props
;
const
{
hiddenSeries
}
=
state
;
// Deduplicate series as visibility tracks the alias property
const
oneSeriesVisible
=
hiddenSeries
.
size
===
new
Set
(
data
.
map
(
d
=>
d
.
alias
)).
size
-
1
;
let
nextHiddenSeries
=
new
Set
();
if
(
exclusive
)
{
if
(
hiddenSeries
.
has
(
series
.
alias
)
||
!
oneSeriesVisible
)
{
nextHiddenSeries
=
new
Set
(
data
.
filter
(
d
=>
d
.
alias
!==
series
.
alias
).
map
(
d
=>
d
.
alias
));
}
}
else
{
// Prune hidden series no longer part of those available from the most recent query
const
availableSeries
=
new
Set
(
data
.
map
(
d
=>
d
.
alias
));
nextHiddenSeries
=
intersect
(
new
Set
(
hiddenSeries
),
availableSeries
);
if
(
nextHiddenSeries
.
has
(
series
.
alias
))
{
nextHiddenSeries
.
delete
(
series
.
alias
);
}
else
{
nextHiddenSeries
.
add
(
series
.
alias
);
}
}
if
(
onToggleSeries
)
{
onToggleSeries
(
series
.
alias
,
nextHiddenSeries
);
}
return
{
hiddenSeries
:
nextHiddenSeries
,
};
},
this
.
draw
);
};
draw
()
{
const
{
userOptions
=
{}
}
=
this
.
props
;
const
{
hiddenSeries
}
=
this
.
state
;
...
...
@@ -210,7 +176,7 @@ export class Graph extends PureComponent<GraphProps, GraphState> {
if
(
data
&&
data
.
length
>
0
)
{
series
=
data
.
filter
((
ts
:
TimeSeries
)
=>
!
hiddenSeries
.
has
(
ts
.
alias
)
)
.
filter
((
ts
:
TimeSeries
)
=>
hiddenSeries
.
indexOf
(
ts
.
alias
)
===
-
1
)
.
map
((
ts
:
TimeSeries
)
=>
({
color
:
ts
.
color
,
label
:
ts
.
label
,
...
...
@@ -229,11 +195,57 @@ export class Graph extends PureComponent<GraphProps, GraphState> {
$
.
plot
(
$el
,
series
,
options
);
}
render
()
{
const
{
height
=
100
,
id
=
'graph'
}
=
this
.
props
;
getLegendItems
=
():
LegendItem
[]
=>
{
const
{
hiddenSeries
}
=
this
.
state
;
const
data
=
this
.
getGraphData
();
return
data
.
map
(
series
=>
{
return
{
label
:
series
.
alias
,
color
:
series
.
color
,
isVisible
:
hiddenSeries
.
indexOf
(
series
.
alias
)
===
-
1
,
yAxis
:
1
,
};
});
};
onSeriesToggle
(
label
:
string
,
event
:
React
.
MouseEvent
<
HTMLElement
>
)
{
// This implementation is more or less a copy of GraphPanel's logic.
// TODO: we need to use Graph's panel controller or split it into smaller
// controllers to remove code duplication. Right now we cant easily use that, since Explore
// is not using SeriesData for graph yet
const
exclusive
=
event
.
ctrlKey
||
event
.
metaKey
||
event
.
shiftKey
;
this
.
setState
((
state
,
props
)
=>
{
const
{
data
}
=
props
;
let
nextHiddenSeries
=
[];
if
(
exclusive
)
{
// Toggling series with key makes the series itself to toggle
if
(
state
.
hiddenSeries
.
indexOf
(
label
)
>
-
1
)
{
nextHiddenSeries
=
state
.
hiddenSeries
.
filter
(
series
=>
series
!==
label
);
}
else
{
nextHiddenSeries
=
state
.
hiddenSeries
.
concat
([
label
]);
}
}
else
{
// Toggling series with out key toggles all the series but the clicked one
const
allSeriesLabels
=
data
.
map
(
series
=>
series
.
label
);
if
(
state
.
hiddenSeries
.
length
+
1
===
allSeriesLabels
.
length
)
{
nextHiddenSeries
=
[];
}
else
{
nextHiddenSeries
=
difference
(
allSeriesLabels
,
[
label
]);
}
}
return
{
hiddenSeries
:
nextHiddenSeries
,
};
});
}
render
()
{
const
{
height
=
100
,
id
=
'graph'
}
=
this
.
props
;
return
(
<>
{
this
.
props
.
data
&&
this
.
props
.
data
.
length
>
MAX_NUMBER_OF_TIME_SERIES
&&
!
this
.
state
.
showAllTimeSeries
&&
(
...
...
@@ -246,7 +258,15 @@ export class Graph extends PureComponent<GraphProps, GraphState> {
</
div
>
)
}
<
div
id=
{
id
}
className=
"explore-graph"
style=
{
{
height
}
}
/>
<
Legend
data=
{
data
}
hiddenSeries=
{
hiddenSeries
}
onToggleSeries=
{
this
.
onToggleSeries
}
/>
<
GraphLegend
items=
{
this
.
getLegendItems
()
}
displayMode=
{
LegendDisplayMode
.
List
}
placement=
"under"
onLabelClick=
{
(
item
,
event
)
=>
{
this
.
onSeriesToggle
(
item
.
label
,
event
);
}
}
/>
</>
);
}
...
...
public/app/features/explore/Legend.tsx
deleted
100644 → 0
View file @
13f137a1
import
React
,
{
MouseEvent
,
PureComponent
}
from
'react'
;
import
classNames
from
'classnames'
;
import
{
TimeSeries
}
from
'app/core/core'
;
interface
LegendProps
{
data
:
TimeSeries
[];
hiddenSeries
:
Set
<
string
>
;
onToggleSeries
?:
(
series
:
TimeSeries
,
exclusive
:
boolean
)
=>
void
;
}
interface
LegendItemProps
{
hidden
:
boolean
;
onClickLabel
?:
(
series
:
TimeSeries
,
event
:
MouseEvent
)
=>
void
;
series
:
TimeSeries
;
}
class
LegendItem
extends
PureComponent
<
LegendItemProps
>
{
onClickLabel
=
e
=>
this
.
props
.
onClickLabel
(
this
.
props
.
series
,
e
);
render
()
{
const
{
hidden
,
series
}
=
this
.
props
;
const
seriesClasses
=
classNames
({
'graph-legend-series-hidden'
:
hidden
,
});
return
(
<
div
className=
{
`graph-legend-series ${seriesClasses}`
}
>
<
div
className=
"graph-legend-icon"
>
<
i
className=
"fa fa-minus pointer"
style=
{
{
color
:
series
.
color
}
}
/>
</
div
>
<
a
className=
"graph-legend-alias pointer"
title=
{
series
.
alias
}
onClick=
{
this
.
onClickLabel
}
>
{
series
.
alias
}
</
a
>
</
div
>
);
}
}
export
default
class
Legend
extends
PureComponent
<
LegendProps
>
{
static
defaultProps
=
{
onToggleSeries
:
()
=>
{},
};
onClickLabel
=
(
series
:
TimeSeries
,
event
:
MouseEvent
)
=>
{
const
{
onToggleSeries
}
=
this
.
props
;
const
exclusive
=
event
.
ctrlKey
||
event
.
metaKey
||
event
.
shiftKey
;
onToggleSeries
(
series
,
!
exclusive
);
};
render
()
{
const
{
data
,
hiddenSeries
}
=
this
.
props
;
const
items
=
data
||
[];
return
(
<
div
className=
"graph-legend ps"
>
{
items
.
map
((
series
,
i
)
=>
(
<
LegendItem
hidden=
{
hiddenSeries
.
has
(
series
.
alias
)
}
// Workaround to resolve conflicts since series visibility tracks the alias property
key=
{
`${series.id}-${i}`
}
onClickLabel=
{
this
.
onClickLabel
}
series=
{
series
}
/>
))
}
</
div
>
);
}
}
public/app/features/explore/__snapshots__/Graph.test.tsx.snap
View file @
34f9b3ff
This diff is collapsed.
Click to expand it.
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