mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Explore: Context tooltip to copy labels and values from graph (#21405)
This commit is contained in:
parent
26f72ccc4e
commit
c738a889ed
8
packages/grafana-data/src/types/flot.ts
Normal file
8
packages/grafana-data/src/types/flot.ts
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
export interface FlotDataPoint {
|
||||||
|
dataIndex: number;
|
||||||
|
datapoint: number[];
|
||||||
|
pageX: number;
|
||||||
|
pageY: number;
|
||||||
|
series: any;
|
||||||
|
seriesIndex: number;
|
||||||
|
}
|
@ -22,6 +22,7 @@ export * from './thresholds';
|
|||||||
export * from './fieldColor';
|
export * from './fieldColor';
|
||||||
export * from './theme';
|
export * from './theme';
|
||||||
export * from './orgs';
|
export * from './orgs';
|
||||||
|
export * from './flot';
|
||||||
|
|
||||||
import * as AppEvents from './appEvents';
|
import * as AppEvents from './appEvents';
|
||||||
import { AppEvent } from './appEvents';
|
import { AppEvent } from './appEvents';
|
||||||
|
@ -5,7 +5,6 @@ import { TimeRange } from '../types/time';
|
|||||||
|
|
||||||
// Types
|
// Types
|
||||||
// import { NullValueMode, GraphSeriesValue, Field, TimeRange } from '@grafana/data';
|
// import { NullValueMode, GraphSeriesValue, Field, TimeRange } from '@grafana/data';
|
||||||
|
|
||||||
export interface FlotPairsOptions {
|
export interface FlotPairsOptions {
|
||||||
xField: Field;
|
xField: Field;
|
||||||
yField: Field;
|
yField: Field;
|
||||||
|
@ -22,6 +22,7 @@ const getTooltipContainerStyles = stylesFactory((theme: GrafanaTheme) => {
|
|||||||
max-width: 800px;
|
max-width: 800px;
|
||||||
padding: ${theme.spacing.sm};
|
padding: ${theme.spacing.sm};
|
||||||
border-radius: ${theme.border.radius.sm};
|
border-radius: ${theme.border.radius.sm};
|
||||||
|
z-index: ${theme.zIndex.tooltip};
|
||||||
`,
|
`,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
@ -25,7 +25,7 @@ export interface ContextMenuProps {
|
|||||||
y: number;
|
y: number;
|
||||||
onClose: () => void;
|
onClose: () => void;
|
||||||
items?: ContextMenuGroup[];
|
items?: ContextMenuGroup[];
|
||||||
renderHeader?: () => JSX.Element;
|
renderHeader?: () => React.ReactNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
const getContextMenuStyles = stylesFactory((theme: GrafanaTheme) => {
|
const getContextMenuStyles = stylesFactory((theme: GrafanaTheme) => {
|
||||||
@ -181,10 +181,11 @@ export const ContextMenu: React.FC<ContextMenuProps> = React.memo(({ x, y, onClo
|
|||||||
});
|
});
|
||||||
|
|
||||||
const styles = getContextMenuStyles(theme);
|
const styles = getContextMenuStyles(theme);
|
||||||
|
const header = renderHeader && renderHeader();
|
||||||
return (
|
return (
|
||||||
<Portal>
|
<Portal>
|
||||||
<div ref={menuRef} style={positionStyles} className={styles.wrapper}>
|
<div ref={menuRef} style={positionStyles} className={styles.wrapper}>
|
||||||
{renderHeader && <div className={styles.header}>{renderHeader()}</div>}
|
{header && <div className={styles.header}>{header}</div>}
|
||||||
<List
|
<List
|
||||||
items={items || []}
|
items={items || []}
|
||||||
renderItem={(item, index) => {
|
renderItem={(item, index) => {
|
||||||
|
@ -3,11 +3,20 @@ import $ from 'jquery';
|
|||||||
import React, { PureComponent } from 'react';
|
import React, { PureComponent } from 'react';
|
||||||
import uniqBy from 'lodash/uniqBy';
|
import uniqBy from 'lodash/uniqBy';
|
||||||
// Types
|
// Types
|
||||||
import { TimeRange, GraphSeriesXY, TimeZone, DefaultTimeZone, createDimension } from '@grafana/data';
|
import {
|
||||||
|
TimeRange,
|
||||||
|
GraphSeriesXY,
|
||||||
|
TimeZone,
|
||||||
|
DefaultTimeZone,
|
||||||
|
createDimension,
|
||||||
|
DateTimeInput,
|
||||||
|
dateTime,
|
||||||
|
} from '@grafana/data';
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import { FlotPosition, FlotItem } from './types';
|
import { FlotPosition, FlotItem } from './types';
|
||||||
import { TooltipProps, TooltipContentProps, ActiveDimensions, Tooltip } from '../Chart/Tooltip';
|
import { TooltipProps, TooltipContentProps, ActiveDimensions, Tooltip } from '../Chart/Tooltip';
|
||||||
import { GraphTooltip } from './GraphTooltip/GraphTooltip';
|
import { GraphTooltip } from './GraphTooltip/GraphTooltip';
|
||||||
|
import { GraphContextMenu, GraphContextMenuProps, ContextDimensions } from './GraphContextMenu';
|
||||||
import { GraphDimensions } from './GraphTooltip/types';
|
import { GraphDimensions } from './GraphTooltip/types';
|
||||||
|
|
||||||
export interface GraphProps {
|
export interface GraphProps {
|
||||||
@ -27,8 +36,11 @@ export interface GraphProps {
|
|||||||
|
|
||||||
interface GraphState {
|
interface GraphState {
|
||||||
pos?: FlotPosition;
|
pos?: FlotPosition;
|
||||||
|
contextPos?: FlotPosition;
|
||||||
isTooltipVisible: boolean;
|
isTooltipVisible: boolean;
|
||||||
|
isContextVisible: boolean;
|
||||||
activeItem?: FlotItem<GraphSeriesXY>;
|
activeItem?: FlotItem<GraphSeriesXY>;
|
||||||
|
contextItem?: FlotItem<GraphSeriesXY>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Graph extends PureComponent<GraphProps, GraphState> {
|
export class Graph extends PureComponent<GraphProps, GraphState> {
|
||||||
@ -42,6 +54,7 @@ export class Graph extends PureComponent<GraphProps, GraphState> {
|
|||||||
|
|
||||||
state: GraphState = {
|
state: GraphState = {
|
||||||
isTooltipVisible: false,
|
isTooltipVisible: false,
|
||||||
|
isContextVisible: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
element: HTMLElement | null = null;
|
element: HTMLElement | null = null;
|
||||||
@ -59,6 +72,7 @@ export class Graph extends PureComponent<GraphProps, GraphState> {
|
|||||||
this.$element = $(this.element);
|
this.$element = $(this.element);
|
||||||
this.$element.bind('plotselected', this.onPlotSelected);
|
this.$element.bind('plotselected', this.onPlotSelected);
|
||||||
this.$element.bind('plothover', this.onPlotHover);
|
this.$element.bind('plothover', this.onPlotHover);
|
||||||
|
this.$element.bind('plotclick', this.onPlotClick);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -81,6 +95,15 @@ export class Graph extends PureComponent<GraphProps, GraphState> {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
onPlotClick = (event: JQueryEventObject, contextPos: FlotPosition, item?: FlotItem<GraphSeriesXY>) => {
|
||||||
|
this.setState({
|
||||||
|
isContextVisible: true,
|
||||||
|
isTooltipVisible: false,
|
||||||
|
contextItem: item,
|
||||||
|
contextPos,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
getYAxes(series: GraphSeriesXY[]) {
|
getYAxes(series: GraphSeriesXY[]) {
|
||||||
if (series.length === 0) {
|
if (series.length === 0) {
|
||||||
return [{ show: true, min: -1, max: 1 }];
|
return [{ show: true, min: -1, max: 1 }];
|
||||||
@ -179,6 +202,68 @@ export class Graph extends PureComponent<GraphProps, GraphState> {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
renderContextMenu = () => {
|
||||||
|
const { series } = this.props;
|
||||||
|
const { contextPos, contextItem, isContextVisible } = this.state;
|
||||||
|
|
||||||
|
if (!isContextVisible || !contextPos || !contextItem || series.length === 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Indicates column(field) index in y-axis dimension
|
||||||
|
const seriesIndex = contextItem ? contextItem.series.seriesIndex : 0;
|
||||||
|
// Indicates row index in context field values
|
||||||
|
const rowIndex = contextItem ? contextItem.dataIndex : undefined;
|
||||||
|
|
||||||
|
const contextDimensions: ContextDimensions<GraphDimensions> = {
|
||||||
|
// Described x-axis context item
|
||||||
|
xAxis: [seriesIndex, rowIndex],
|
||||||
|
// Describes y-axis context item
|
||||||
|
yAxis: contextItem ? [contextItem.series.seriesIndex, contextItem.dataIndex] : null,
|
||||||
|
};
|
||||||
|
|
||||||
|
const dimensions: GraphDimensions = {
|
||||||
|
// time/value dimension columns are index-aligned - see getGraphSeriesModel
|
||||||
|
xAxis: createDimension(
|
||||||
|
'xAxis',
|
||||||
|
series.map(s => s.timeField)
|
||||||
|
),
|
||||||
|
yAxis: createDimension(
|
||||||
|
'yAxis',
|
||||||
|
series.map(s => s.valueField)
|
||||||
|
),
|
||||||
|
};
|
||||||
|
|
||||||
|
const formatDate = (date: DateTimeInput, format?: string) => {
|
||||||
|
return dateTime(date)?.format(format);
|
||||||
|
};
|
||||||
|
|
||||||
|
const closeContext = () => this.setState({ isContextVisible: false });
|
||||||
|
|
||||||
|
const getContextMenuSource = () => {
|
||||||
|
return {
|
||||||
|
datapoint: contextItem.datapoint,
|
||||||
|
dataIndex: contextItem.dataIndex,
|
||||||
|
series: contextItem.series,
|
||||||
|
seriesIndex: contextItem.series.seriesIndex,
|
||||||
|
pageX: contextPos.pageX,
|
||||||
|
pageY: contextPos.pageY,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const contextContentProps: GraphContextMenuProps = {
|
||||||
|
x: contextPos.pageX,
|
||||||
|
y: contextPos.pageY,
|
||||||
|
onClose: closeContext,
|
||||||
|
getContextMenuSource: getContextMenuSource,
|
||||||
|
formatSourceDate: formatDate,
|
||||||
|
dimensions,
|
||||||
|
contextDimensions,
|
||||||
|
};
|
||||||
|
|
||||||
|
return <GraphContextMenu {...contextContentProps} />;
|
||||||
|
};
|
||||||
|
|
||||||
getBarWidth = () => {
|
getBarWidth = () => {
|
||||||
const { series } = this.props;
|
const { series } = this.props;
|
||||||
return Math.min(...series.map(s => s.timeStep));
|
return Math.min(...series.map(s => s.timeStep));
|
||||||
@ -285,6 +370,8 @@ export class Graph extends PureComponent<GraphProps, GraphState> {
|
|||||||
render() {
|
render() {
|
||||||
const { height, width, series } = this.props;
|
const { height, width, series } = this.props;
|
||||||
const noDataToBeDisplayed = series.length === 0;
|
const noDataToBeDisplayed = series.length === 0;
|
||||||
|
const tooltip = this.renderTooltip();
|
||||||
|
const context = this.renderContextMenu();
|
||||||
return (
|
return (
|
||||||
<div className="graph-panel">
|
<div className="graph-panel">
|
||||||
<div
|
<div
|
||||||
@ -295,9 +382,9 @@ export class Graph extends PureComponent<GraphProps, GraphState> {
|
|||||||
this.setState({ isTooltipVisible: false });
|
this.setState({ isTooltipVisible: false });
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{noDataToBeDisplayed && <div className="datapoints-warning">No data</div>}
|
{noDataToBeDisplayed && <div className="datapoints-warning">No data</div>}
|
||||||
{this.renderTooltip()}
|
{tooltip}
|
||||||
|
{context}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,99 @@
|
|||||||
|
import React, { useContext } from 'react';
|
||||||
|
import { ContextMenu, ContextMenuProps } from '../ContextMenu/ContextMenu';
|
||||||
|
import { ThemeContext } from '../../themes';
|
||||||
|
import { SeriesIcon } from '../Legend/SeriesIcon';
|
||||||
|
import { GraphDimensions } from './GraphTooltip/types';
|
||||||
|
import {
|
||||||
|
DateTimeInput,
|
||||||
|
FlotDataPoint,
|
||||||
|
getValueFromDimension,
|
||||||
|
getDisplayProcessor,
|
||||||
|
formattedValueToString,
|
||||||
|
Dimensions,
|
||||||
|
MS_DATE_TIME_FORMAT,
|
||||||
|
DEFAULT_DATE_TIME_FORMAT,
|
||||||
|
} from '@grafana/data';
|
||||||
|
import { css } from 'emotion';
|
||||||
|
|
||||||
|
export type ContextDimensions<T extends Dimensions = any> = { [key in keyof T]: [number, number | undefined] | null };
|
||||||
|
|
||||||
|
export type GraphContextMenuProps = ContextMenuProps & {
|
||||||
|
getContextMenuSource: () => FlotDataPoint | null;
|
||||||
|
formatSourceDate: (date: DateTimeInput, format?: string) => string;
|
||||||
|
dimensions?: GraphDimensions;
|
||||||
|
contextDimensions?: ContextDimensions;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const GraphContextMenu: React.FC<GraphContextMenuProps> = ({
|
||||||
|
getContextMenuSource,
|
||||||
|
formatSourceDate,
|
||||||
|
items,
|
||||||
|
dimensions,
|
||||||
|
contextDimensions,
|
||||||
|
...otherProps
|
||||||
|
}) => {
|
||||||
|
const theme = useContext(ThemeContext);
|
||||||
|
const source = getContextMenuSource();
|
||||||
|
|
||||||
|
// Do not render items that do not have label specified
|
||||||
|
const itemsToRender = items
|
||||||
|
? items.map(group => ({
|
||||||
|
...group,
|
||||||
|
items: group.items.filter(item => item.label),
|
||||||
|
}))
|
||||||
|
: [];
|
||||||
|
|
||||||
|
const renderHeader = () => {
|
||||||
|
if (!source) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If dimensions supplied, we can calculate and display value
|
||||||
|
let value;
|
||||||
|
if (dimensions?.yAxis && contextDimensions?.yAxis?.[1]) {
|
||||||
|
const valueFromDimensions = getValueFromDimension(
|
||||||
|
dimensions.yAxis,
|
||||||
|
contextDimensions.yAxis[0],
|
||||||
|
contextDimensions.yAxis[1]
|
||||||
|
);
|
||||||
|
const display = source.series.valueField.display ?? getDisplayProcessor({ field: source.series.valueField });
|
||||||
|
value = display(valueFromDimensions);
|
||||||
|
}
|
||||||
|
|
||||||
|
const timeFormat = source.series.hasMsResolution ? MS_DATE_TIME_FORMAT : DEFAULT_DATE_TIME_FORMAT;
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={css`
|
||||||
|
padding: ${theme.spacing.xs} ${theme.spacing.sm};
|
||||||
|
font-size: ${theme.typography.size.sm};
|
||||||
|
z-index: ${theme.zIndex.tooltip};
|
||||||
|
`}
|
||||||
|
>
|
||||||
|
<strong>{formatSourceDate(source.datapoint[0], timeFormat)}</strong>
|
||||||
|
<div>
|
||||||
|
<SeriesIcon color={source.series.color} />
|
||||||
|
<span
|
||||||
|
className={css`
|
||||||
|
white-space: nowrap;
|
||||||
|
padding-left: ${theme.spacing.xs};
|
||||||
|
`}
|
||||||
|
>
|
||||||
|
{source.series.alias || source.series.label}
|
||||||
|
</span>
|
||||||
|
{value && (
|
||||||
|
<span
|
||||||
|
className={css`
|
||||||
|
white-space: nowrap;
|
||||||
|
padding-left: ${theme.spacing.md};
|
||||||
|
`}
|
||||||
|
>
|
||||||
|
{formattedValueToString(value)}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
return <ContextMenu {...otherProps} items={itemsToRender} renderHeader={renderHeader} />;
|
||||||
|
};
|
@ -22,6 +22,7 @@ const getSeriesTableRowStyles = stylesFactory((theme: GrafanaTheme) => {
|
|||||||
`,
|
`,
|
||||||
seriesTableRow: css`
|
seriesTableRow: css`
|
||||||
display: table-row;
|
display: table-row;
|
||||||
|
font-size: ${theme.typography.size.sm};
|
||||||
`,
|
`,
|
||||||
seriesTableCell: css`
|
seriesTableCell: css`
|
||||||
display: table-cell;
|
display: table-cell;
|
||||||
@ -35,6 +36,10 @@ const getSeriesTableRowStyles = stylesFactory((theme: GrafanaTheme) => {
|
|||||||
activeSeries: css`
|
activeSeries: css`
|
||||||
font-weight: ${theme.typography.weight.bold};
|
font-weight: ${theme.typography.weight.bold};
|
||||||
`,
|
`,
|
||||||
|
timestamp: css`
|
||||||
|
font-weight: ${theme.typography.weight.bold};
|
||||||
|
font-size: ${theme.typography.size.sm};
|
||||||
|
`,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -60,9 +65,15 @@ interface SeriesTableProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const SeriesTable: React.FC<SeriesTableProps> = ({ timestamp, series }) => {
|
export const SeriesTable: React.FC<SeriesTableProps> = ({ timestamp, series }) => {
|
||||||
|
const theme = useTheme();
|
||||||
|
const styles = getSeriesTableRowStyles(theme);
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{timestamp && <div aria-label="Timestamp">{timestamp}</div>}
|
{timestamp && (
|
||||||
|
<div className={styles.timestamp} aria-label="Timestamp">
|
||||||
|
{timestamp}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
{series.map(s => {
|
{series.map(s => {
|
||||||
return <SeriesTableRow isActive={s.isActive} label={s.label} color={s.color} value={s.value} key={s.label} />;
|
return <SeriesTableRow isActive={s.isActive} label={s.label} color={s.color} value={s.value} key={s.label} />;
|
||||||
})}
|
})}
|
||||||
|
@ -66,6 +66,7 @@ export { Gauge } from './Gauge/Gauge';
|
|||||||
export { Graph } from './Graph/Graph';
|
export { Graph } from './Graph/Graph';
|
||||||
export { GraphLegend } from './Graph/GraphLegend';
|
export { GraphLegend } from './Graph/GraphLegend';
|
||||||
export { GraphWithLegend } from './Graph/GraphWithLegend';
|
export { GraphWithLegend } from './Graph/GraphWithLegend';
|
||||||
|
export { GraphContextMenu } from './Graph/GraphContextMenu';
|
||||||
export { BarGauge, BarGaugeDisplayMode } from './BarGauge/BarGauge';
|
export { BarGauge, BarGaugeDisplayMode } from './BarGauge/BarGauge';
|
||||||
export { GraphTooltipOptions } from './Graph/GraphTooltip/types';
|
export { GraphTooltipOptions } from './Graph/GraphTooltip/types';
|
||||||
export { VizRepeater } from './VizRepeater/VizRepeater';
|
export { VizRepeater } from './VizRepeater/VizRepeater';
|
||||||
|
@ -15,10 +15,10 @@ import {
|
|||||||
UnitPicker,
|
UnitPicker,
|
||||||
DataLinksEditor,
|
DataLinksEditor,
|
||||||
DataSourceHttpSettings,
|
DataSourceHttpSettings,
|
||||||
|
GraphContextMenu,
|
||||||
} from '@grafana/ui';
|
} from '@grafana/ui';
|
||||||
import { FunctionEditor } from 'app/plugins/datasource/graphite/FunctionEditor';
|
import { FunctionEditor } from 'app/plugins/datasource/graphite/FunctionEditor';
|
||||||
import { SearchField } from './components/search/SearchField';
|
import { SearchField } from './components/search/SearchField';
|
||||||
import { GraphContextMenu } from 'app/plugins/panel/graph/GraphContextMenu';
|
|
||||||
import ReactProfileWrapper from 'app/features/profile/ReactProfileWrapper';
|
import ReactProfileWrapper from 'app/features/profile/ReactProfileWrapper';
|
||||||
import { LokiAnnotationsQueryEditor } from '../plugins/datasource/loki/components/AnnotationsQueryEditor';
|
import { LokiAnnotationsQueryEditor } from '../plugins/datasource/loki/components/AnnotationsQueryEditor';
|
||||||
import { HelpModal } from './components/help/HelpModal';
|
import { HelpModal } from './components/help/HelpModal';
|
||||||
|
@ -1,62 +0,0 @@
|
|||||||
import React, { useContext } from 'react';
|
|
||||||
import { FlotDataPoint } from './GraphContextMenuCtrl';
|
|
||||||
import { ContextMenu, ContextMenuProps, SeriesIcon, ThemeContext } from '@grafana/ui';
|
|
||||||
import { DateTimeInput } from '@grafana/data';
|
|
||||||
import { css } from 'emotion';
|
|
||||||
|
|
||||||
type GraphContextMenuProps = ContextMenuProps & {
|
|
||||||
getContextMenuSource: () => FlotDataPoint | null;
|
|
||||||
formatSourceDate: (date: DateTimeInput, format?: string) => string;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const GraphContextMenu: React.FC<GraphContextMenuProps> = ({
|
|
||||||
getContextMenuSource,
|
|
||||||
formatSourceDate,
|
|
||||||
items,
|
|
||||||
...otherProps
|
|
||||||
}) => {
|
|
||||||
const theme = useContext(ThemeContext);
|
|
||||||
const source = getContextMenuSource();
|
|
||||||
|
|
||||||
// Do not render items that do not have label specified
|
|
||||||
const itemsToRender = items
|
|
||||||
? items.map(group => ({
|
|
||||||
...group,
|
|
||||||
items: group.items.filter(item => item.label),
|
|
||||||
}))
|
|
||||||
: [];
|
|
||||||
|
|
||||||
const renderHeader = source
|
|
||||||
? () => {
|
|
||||||
if (!source) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
const timeFormat = source.series.hasMsResolution ? 'YYYY-MM-DD HH:mm:ss.SSS' : 'YYYY-MM-DD HH:mm:ss';
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
className={css`
|
|
||||||
padding: ${theme.spacing.xs} ${theme.spacing.sm};
|
|
||||||
font-size: ${theme.typography.size.sm};
|
|
||||||
`}
|
|
||||||
>
|
|
||||||
<strong>{formatSourceDate(source.datapoint[0], timeFormat)}</strong>
|
|
||||||
<div>
|
|
||||||
<SeriesIcon color={source.series.color} />
|
|
||||||
<span
|
|
||||||
className={css`
|
|
||||||
white-space: nowrap;
|
|
||||||
padding-left: ${theme.spacing.xs};
|
|
||||||
`}
|
|
||||||
>
|
|
||||||
{source.series.alias}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
: null;
|
|
||||||
|
|
||||||
return <ContextMenu {...otherProps} items={itemsToRender} renderHeader={renderHeader} />;
|
|
||||||
};
|
|
@ -1,13 +1,5 @@
|
|||||||
import { ContextMenuItem } from '@grafana/ui';
|
import { ContextMenuItem } from '@grafana/ui';
|
||||||
|
import { FlotDataPoint } from '@grafana/data';
|
||||||
export interface FlotDataPoint {
|
|
||||||
dataIndex: number;
|
|
||||||
datapoint: number[];
|
|
||||||
pageX: number;
|
|
||||||
pageY: number;
|
|
||||||
series: any;
|
|
||||||
seriesIndex: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
export class GraphContextMenuCtrl {
|
export class GraphContextMenuCtrl {
|
||||||
private source?: FlotDataPoint | null;
|
private source?: FlotDataPoint | null;
|
||||||
|
Loading…
Reference in New Issue
Block a user