onSeriesColorChange(item.label, color)}
+ onColorChange={color => {
+ if (onSeriesColorChange) {
+ onSeriesColorChange(item.label, color);
+ }
+ }}
onToggleAxis={onToggleAxis}
yAxis={item.yAxis}
/>
onLabelClick(item, event)}
+ onClick={event => {
+ if (onLabelClick) {
+ onLabelClick(item, event);
+ }
+ }}
className={css`
cursor: pointer;
white-space: nowrap;
diff --git a/packages/grafana-ui/src/components/Legend/LegendList.tsx b/packages/grafana-ui/src/components/Legend/LegendList.tsx
index d103aa3ed8067..f220b10a58765 100644
--- a/packages/grafana-ui/src/components/Legend/LegendList.tsx
+++ b/packages/grafana-ui/src/components/Legend/LegendList.tsx
@@ -28,7 +28,7 @@ export const LegendList: React.FunctionComponent = ({
);
};
- const getItemKey = (item: LegendItem) => item.label;
+ const getItemKey = (item: LegendItem) => `${item.label}`;
const styles = {
wrapper: cx(
diff --git a/packages/grafana-ui/src/components/Legend/LegendSeriesIcon.tsx b/packages/grafana-ui/src/components/Legend/LegendSeriesIcon.tsx
index 1913c2e500d25..787b818c00303 100644
--- a/packages/grafana-ui/src/components/Legend/LegendSeriesIcon.tsx
+++ b/packages/grafana-ui/src/components/Legend/LegendSeriesIcon.tsx
@@ -1,8 +1,10 @@
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 = ({
+ disabled,
yAxis,
color,
onColorChange,
onToggleAxis,
}) => {
- return (
+ let iconProps: SeriesIconProps = {
+ color,
+ };
+
+ if (!disabled) {
+ iconProps = {
+ ...iconProps,
+ className: 'pointer',
+ };
+ }
+
+ return disabled ? (
+
+
+
+ ) : (
=
>
{({ ref, showColorPicker, hideColorPicker }) => (
-
+
)}
diff --git a/packages/grafana-ui/src/components/Legend/SeriesIcon.tsx b/packages/grafana-ui/src/components/Legend/SeriesIcon.tsx
index 091d79f7fd0d0..70709a0fcd5cf 100644
--- a/packages/grafana-ui/src/components/Legend/SeriesIcon.tsx
+++ b/packages/grafana-ui/src/components/Legend/SeriesIcon.tsx
@@ -1,5 +1,10 @@
import React from 'react';
+import { cx } from 'emotion';
-export const SeriesIcon: React.FunctionComponent<{ color: string }> = ({ color }) => {
- return ;
+export interface SeriesIconProps {
+ color: string;
+ className?: string;
+}
+export const SeriesIcon: React.FunctionComponent = ({ color, className }) => {
+ return ;
};
diff --git a/packages/grafana-ui/src/components/index.ts b/packages/grafana-ui/src/components/index.ts
index 5a4f58626c6a7..860dd5a97ab0e 100644
--- a/packages/grafana-ui/src/components/index.ts
+++ b/packages/grafana-ui/src/components/index.ts
@@ -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';
diff --git a/public/app/features/explore/Graph.tsx b/public/app/features/explore/Graph.tsx
index f9c48fc92c722..b5cdca318afa6 100644
--- a/public/app/features/explore/Graph.tsx
+++ b/public/app/features/explore/Graph.tsx
@@ -1,17 +1,15 @@
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;
+ hiddenSeries: string[];
showAllTimeSeries: boolean;
}
@@ -98,11 +96,11 @@ export class Graph extends PureComponent {
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 {
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 {
);
};
- 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 {
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 {
$.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) {
+ // 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 {
)}
-
+
+ {
+ this.onSeriesToggle(item.label, event);
+ }}
+ />
>
);
}
diff --git a/public/app/features/explore/Legend.tsx b/public/app/features/explore/Legend.tsx
deleted file mode 100644
index 3b67aa74d9173..0000000000000
--- a/public/app/features/explore/Legend.tsx
+++ /dev/null
@@ -1,66 +0,0 @@
-import React, { MouseEvent, PureComponent } from 'react';
-import classNames from 'classnames';
-import { TimeSeries } from 'app/core/core';
-
-interface LegendProps {
- data: TimeSeries[];
- hiddenSeries: Set;
- onToggleSeries?: (series: TimeSeries, exclusive: boolean) => void;
-}
-
-interface LegendItemProps {
- hidden: boolean;
- onClickLabel?: (series: TimeSeries, event: MouseEvent) => void;
- series: TimeSeries;
-}
-
-class LegendItem extends PureComponent {
- onClickLabel = e => this.props.onClickLabel(this.props.series, e);
-
- render() {
- const { hidden, series } = this.props;
- const seriesClasses = classNames({
- 'graph-legend-series-hidden': hidden,
- });
- return (
-
- );
- }
-}
-
-export default class Legend extends PureComponent {
- 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 (
-
- {items.map((series, i) => (
-
- ))}
-
- );
- }
-}
diff --git a/public/app/features/explore/__snapshots__/Graph.test.tsx.snap b/public/app/features/explore/__snapshots__/Graph.test.tsx.snap
index c38fb26a25232..d43f94856055c 100644
--- a/public/app/features/explore/__snapshots__/Graph.test.tsx.snap
+++ b/public/app/features/explore/__snapshots__/Graph.test.tsx.snap
@@ -11,450 +11,128 @@ exports[`Render should render component 1`] = `
}
}
/>
-
`;
@@ -484,473 +162,134 @@ exports[`Render should render component with disclaimer 1`] = `
}
}
/>
-
`;
@@ -966,10 +305,11 @@ exports[`Render should show query return no time series 1`] = `
}
}
/>
-
`;
From 79ac3fd699476328e538d4d1f73708fc0fc66715 Mon Sep 17 00:00:00 2001
From: Oleg Gaidarenko
Date: Tue, 14 May 2019 10:18:28 +0300
Subject: [PATCH 06/14] Chore: remove use of `== false` (#17036)
Interestingly enough, golint or revive doesn't not prohibit
the use that construction :)
Ref #17035
---
pkg/middleware/auth_proxy.go | 6 +++---
pkg/middleware/auth_proxy/auth_proxy.go | 2 +-
pkg/services/ldap/settings.go | 7 ++++---
3 files changed, 8 insertions(+), 7 deletions(-)
diff --git a/pkg/middleware/auth_proxy.go b/pkg/middleware/auth_proxy.go
index 6dc69dda8b5f8..d3d8d8e77d8aa 100644
--- a/pkg/middleware/auth_proxy.go
+++ b/pkg/middleware/auth_proxy.go
@@ -20,17 +20,17 @@ func initContextWithAuthProxy(store *remotecache.RemoteCache, ctx *m.ReqContext,
})
// Bail if auth proxy is not enabled
- if auth.IsEnabled() == false {
+ if !auth.IsEnabled() {
return false
}
// If the there is no header - we can't move forward
- if auth.HasHeader() == false {
+ if !auth.HasHeader() {
return false
}
// Check if allowed to continue with this IP
- if result, err := auth.IsAllowedIP(); result == false {
+ if result, err := auth.IsAllowedIP(); !result {
ctx.Handle(407, err.Error(), err.DetailsError)
return true
}
diff --git a/pkg/middleware/auth_proxy/auth_proxy.go b/pkg/middleware/auth_proxy/auth_proxy.go
index b9e71d1b480d4..98bacbeccf47f 100644
--- a/pkg/middleware/auth_proxy/auth_proxy.go
+++ b/pkg/middleware/auth_proxy/auth_proxy.go
@@ -92,7 +92,7 @@ func New(options *Options) *AuthProxy {
func (auth *AuthProxy) IsEnabled() bool {
// Bail if the setting is not enabled
- if auth.enabled == false {
+ if !auth.enabled {
return false
}
diff --git a/pkg/services/ldap/settings.go b/pkg/services/ldap/settings.go
index de2da2402bfef..0a0f66d9d7347 100644
--- a/pkg/services/ldap/settings.go
+++ b/pkg/services/ldap/settings.go
@@ -5,12 +5,12 @@ import (
"sync"
"github.com/BurntSushi/toml"
- "github.com/grafana/grafana/pkg/util/errutil"
"golang.org/x/xerrors"
"github.com/grafana/grafana/pkg/infra/log"
m "github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/setting"
+ "github.com/grafana/grafana/pkg/util/errutil"
)
type Config struct {
@@ -68,9 +68,10 @@ func IsEnabled() bool {
// ReloadConfig reads the config from the disc and caches it.
func ReloadConfig() error {
- if IsEnabled() == false {
+ if !IsEnabled() {
return nil
}
+
loadingMutex.Lock()
defer loadingMutex.Unlock()
@@ -82,7 +83,7 @@ func ReloadConfig() error {
// GetConfig returns the LDAP config if LDAP is enabled otherwise it returns nil. It returns either cached value of
// the config or it reads it and caches it first.
func GetConfig() (*Config, error) {
- if IsEnabled() == false {
+ if !IsEnabled() {
return nil, nil
}
From 51c99fc68d5830a27b5509665dc2b42b88ab00b3 Mon Sep 17 00:00:00 2001
From: Carl Bergquist
Date: Tue, 14 May 2019 10:30:05 +0200
Subject: [PATCH 07/14] Docs: adds note about removing session storage (#17003)
closes #17000
---
docs/sources/installation/upgrading.md | 6 ++++++
docs/sources/tutorials/ha_setup.md | 3 ++-
2 files changed, 8 insertions(+), 1 deletion(-)
diff --git a/docs/sources/installation/upgrading.md b/docs/sources/installation/upgrading.md
index bd2a5434b25fd..22195cb9df503 100644
--- a/docs/sources/installation/upgrading.md
+++ b/docs/sources/installation/upgrading.md
@@ -169,3 +169,9 @@ configuration.
If you're embedding Grafana in a ``, `