mirror of
https://github.com/grafana/grafana.git
synced 2025-01-27 00:37:04 -06:00
PieChart: Unify tooltip to look the way it looks in TimeSeries (#33032)
* feat(piechart): align styles between piechart and graph * feat(piechart): introduce tooltip options to panel and visualisation * feat(piechart): get tooltip options working * feat(piechart): add SeriesTable to visx TooltipInPortal * refactor(piechart): move getTooltipData out of PieSlice * docs(piechart): fix storybook story errors * feat(viztooltip): initial commit of common tooltip types and components * refactor(viztooltip): rename type as enum and update usage * refactor(viztooltip): move chart.tooltip into viztooltip and fix imports and typings * refactor(viztooltip): update import paths and names where used * docs(infotooltip): fix story import paths * docs(piechart): fix typings in story * docs(viztooltip): add public annotations to exported components and types
This commit is contained in:
parent
5f54f2dc00
commit
c2953f3a06
@ -23,7 +23,7 @@ TableSortByFieldState: {
|
||||
desc?: bool
|
||||
} @cuetsy(targetType="interface")
|
||||
|
||||
TooltipMode: "single" | "multi" | "none" @cuetsy(targetType="type")
|
||||
TooltipDisplayMode: "single" | "multi" | "none" @cuetsy(targetType="enum")
|
||||
FieldTextAlignment: "auto" | "left" | "right" | "center" @cuetsy(targetType="type")
|
||||
AxisPlacement: "auto" | "top" | "right" | "bottom" | "left" | "hidden" @cuetsy(targetType="enum")
|
||||
PointVisibility: "auto" | "never" | "always" @cuetsy(targetType="enum")
|
||||
@ -72,9 +72,6 @@ HideSeriesConfig: {
|
||||
} @cuetsy(targetType="interface")
|
||||
LegendPlacement: "bottom" | "right" @cuetsy(targetType="type")
|
||||
LegendDisplayMode: "list" | "table" | "hidden" @cuetsy(targetType="enum")
|
||||
GraphTooltipOptions: {
|
||||
mode: TooltipMode
|
||||
} @cuetsy(targetType="interface")
|
||||
TableFieldOptions: {
|
||||
width?: number
|
||||
align: FieldTextAlignment | *"auto"
|
||||
@ -91,3 +88,6 @@ VizLegendOptions: {
|
||||
placement: LegendPlacement
|
||||
calcs: [string]
|
||||
} @cuetsy(targetType="interface")
|
||||
VizTooltipOptions: {
|
||||
mode: TooltipDisplayMode
|
||||
} @cuetsy(targetType="interface")
|
||||
|
@ -1,7 +0,0 @@
|
||||
import { Tooltip } from './Tooltip';
|
||||
|
||||
const Chart = {
|
||||
Tooltip,
|
||||
};
|
||||
|
||||
export default Chart;
|
@ -1,3 +0,0 @@
|
||||
package grafanaschema
|
||||
|
||||
TooltipMode: "single" | "multi" | "none" @cuetsy(targetType="type")
|
@ -1,6 +0,0 @@
|
||||
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
// NOTE: This file will be auto generated from models.cue
|
||||
// It is currenty hand written but will serve as the target for cuetsy
|
||||
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
export type TooltipMode = 'single' | 'multi' | 'none';
|
@ -1,11 +1,9 @@
|
||||
import React from 'react';
|
||||
import { Graph } from '@grafana/ui';
|
||||
import Chart from '../Chart';
|
||||
import { dateTime, ArrayVector, FieldType, GraphSeriesXY, FieldColorModeId } from '@grafana/data';
|
||||
import { Story } from '@storybook/react';
|
||||
import { withCenteredStory } from '../../utils/storybook/withCenteredStory';
|
||||
import { TooltipContentProps } from '../Chart/Tooltip';
|
||||
import { TooltipMode } from '../Chart/models.gen';
|
||||
import { VizTooltip, TooltipDisplayMode, VizTooltipContentProps } from '../VizTooltip';
|
||||
import { JSONFormatter } from '../JSONFormatter/JSONFormatter';
|
||||
import { GraphProps } from './Graph';
|
||||
|
||||
@ -114,15 +112,15 @@ export default {
|
||||
},
|
||||
};
|
||||
|
||||
export const WithTooltip: Story<GraphProps & { tooltipMode: TooltipMode }> = ({ tooltipMode, ...args }) => {
|
||||
export const WithTooltip: Story<GraphProps & { tooltipMode: TooltipDisplayMode }> = ({ tooltipMode, ...args }) => {
|
||||
return (
|
||||
<Graph {...args}>
|
||||
<Chart.Tooltip mode={tooltipMode} />
|
||||
<VizTooltip mode={tooltipMode} />
|
||||
</Graph>
|
||||
);
|
||||
};
|
||||
|
||||
const CustomGraphTooltip = ({ activeDimensions }: TooltipContentProps) => {
|
||||
const CustomGraphTooltip = ({ activeDimensions }: VizTooltipContentProps) => {
|
||||
return (
|
||||
<div style={{ height: '200px' }}>
|
||||
<div>Showing currently active active dimensions:</div>
|
||||
@ -131,10 +129,13 @@ const CustomGraphTooltip = ({ activeDimensions }: TooltipContentProps) => {
|
||||
);
|
||||
};
|
||||
|
||||
export const WithCustomTooltip: Story<GraphProps & { tooltipMode: TooltipMode }> = ({ tooltipMode, ...args }) => {
|
||||
export const WithCustomTooltip: Story<GraphProps & { tooltipMode: TooltipDisplayMode }> = ({
|
||||
tooltipMode,
|
||||
...args
|
||||
}) => {
|
||||
return (
|
||||
<Graph {...args}>
|
||||
<Chart.Tooltip mode={tooltipMode} tooltipComponent={CustomGraphTooltip} />
|
||||
<VizTooltip mode={tooltipMode} tooltipComponent={CustomGraphTooltip} />
|
||||
</Graph>
|
||||
);
|
||||
};
|
||||
|
@ -1,7 +1,7 @@
|
||||
import React from 'react';
|
||||
import { mount } from 'enzyme';
|
||||
import Graph from './Graph';
|
||||
import Chart from '../Chart';
|
||||
import { VizTooltip, TooltipDisplayMode } from '../VizTooltip';
|
||||
import { GraphSeriesXY, FieldType, ArrayVector, dateTime, FieldColorModeId } from '@grafana/data';
|
||||
|
||||
const series: GraphSeriesXY[] = [
|
||||
@ -92,7 +92,7 @@ describe('Graph', () => {
|
||||
it("doesn't render tooltip when not hovering over a datapoint", () => {
|
||||
const graphWithTooltip = (
|
||||
<Graph {...mockGraphProps()}>
|
||||
<Chart.Tooltip mode="single" />
|
||||
<VizTooltip mode={TooltipDisplayMode.Single} />
|
||||
</Graph>
|
||||
);
|
||||
|
||||
@ -105,7 +105,7 @@ describe('Graph', () => {
|
||||
// Given
|
||||
const graphWithTooltip = (
|
||||
<Graph {...mockGraphProps()}>
|
||||
<Chart.Tooltip mode="single" />
|
||||
<VizTooltip mode={TooltipDisplayMode.Single} />
|
||||
</Graph>
|
||||
);
|
||||
const container = mount(graphWithTooltip);
|
||||
@ -145,7 +145,7 @@ describe('Graph', () => {
|
||||
// Given
|
||||
const graphWithTooltip = (
|
||||
<Graph {...mockGraphProps(true)}>
|
||||
<Chart.Tooltip mode="multi" />
|
||||
<VizTooltip mode={TooltipDisplayMode.Multi} />
|
||||
</Graph>
|
||||
);
|
||||
const container = mount(graphWithTooltip);
|
||||
|
@ -6,11 +6,12 @@ import uniqBy from 'lodash/uniqBy';
|
||||
import { TimeRange, GraphSeriesXY, TimeZone, createDimension } from '@grafana/data';
|
||||
import _ from 'lodash';
|
||||
import { FlotPosition, FlotItem } from './types';
|
||||
import { TooltipProps, TooltipContentProps, ActiveDimensions, Tooltip } from '../Chart/Tooltip';
|
||||
import { VizTooltipProps, VizTooltipContentProps, ActiveDimensions, VizTooltip } from '../VizTooltip';
|
||||
import { GraphTooltip } from './GraphTooltip/GraphTooltip';
|
||||
import { GraphContextMenu, GraphContextMenuProps, ContextDimensions } from './GraphContextMenu';
|
||||
import { GraphDimensions } from './GraphTooltip/types';
|
||||
import { graphTimeFormat, graphTickFormatter } from './utils';
|
||||
import { TooltipDisplayMode } from '../VizTooltip/models.gen';
|
||||
|
||||
export interface GraphProps {
|
||||
ariaLabel?: string;
|
||||
@ -124,7 +125,7 @@ export class Graph extends PureComponent<GraphProps, GraphState> {
|
||||
renderTooltip = () => {
|
||||
const { children, series, timeZone } = this.props;
|
||||
const { pos, activeItem, isTooltipVisible } = this.state;
|
||||
let tooltipElement: React.ReactElement<TooltipProps> | null = null;
|
||||
let tooltipElement: React.ReactElement<VizTooltipProps> | null = null;
|
||||
|
||||
if (!isTooltipVisible || !pos || series.length === 0) {
|
||||
return null;
|
||||
@ -139,15 +140,15 @@ export class Graph extends PureComponent<GraphProps, GraphState> {
|
||||
// @ts-ignore
|
||||
const childType = c && c.type && (c.type.displayName || c.type.name);
|
||||
|
||||
if (childType === Tooltip.displayName) {
|
||||
tooltipElement = c as React.ReactElement<TooltipProps>;
|
||||
if (childType === VizTooltip.displayName) {
|
||||
tooltipElement = c as React.ReactElement<VizTooltipProps>;
|
||||
}
|
||||
});
|
||||
// If no tooltip provided, skip rendering
|
||||
if (!tooltipElement) {
|
||||
return null;
|
||||
}
|
||||
const tooltipElementProps = (tooltipElement as React.ReactElement<TooltipProps>).props;
|
||||
const tooltipElementProps = (tooltipElement as React.ReactElement<VizTooltipProps>).props;
|
||||
|
||||
const tooltipMode = tooltipElementProps.mode || 'single';
|
||||
|
||||
@ -172,7 +173,7 @@ export class Graph extends PureComponent<GraphProps, GraphState> {
|
||||
yAxis: activeItem ? [activeItem.series.seriesIndex, activeItem.dataIndex] : null,
|
||||
};
|
||||
|
||||
const tooltipContentProps: TooltipContentProps<GraphDimensions> = {
|
||||
const tooltipContentProps: VizTooltipContentProps<GraphDimensions> = {
|
||||
dimensions: {
|
||||
// time/value dimension columns are index-aligned - see getGraphSeriesModel
|
||||
xAxis: createDimension(
|
||||
@ -186,13 +187,13 @@ export class Graph extends PureComponent<GraphProps, GraphState> {
|
||||
},
|
||||
activeDimensions,
|
||||
pos,
|
||||
mode: tooltipElementProps.mode || 'single',
|
||||
mode: tooltipElementProps.mode || TooltipDisplayMode.Single,
|
||||
timeZone,
|
||||
};
|
||||
|
||||
const tooltipContent = React.createElement(tooltipContentRenderer, { ...tooltipContentProps });
|
||||
|
||||
return React.cloneElement<TooltipProps>(tooltipElement as React.ReactElement<TooltipProps>, {
|
||||
return React.cloneElement<VizTooltipProps>(tooltipElement as React.ReactElement<VizTooltipProps>, {
|
||||
content: tooltipContent,
|
||||
position: { x: pos.pageX, y: pos.pageY },
|
||||
offset: { x: 10, y: 10 },
|
||||
|
@ -1,10 +1,10 @@
|
||||
import React from 'react';
|
||||
import { TooltipContentProps } from '../../Chart/Tooltip';
|
||||
import { VizTooltipContentProps } from '../../VizTooltip';
|
||||
import { SingleModeGraphTooltip } from './SingleModeGraphTooltip';
|
||||
import { MultiModeGraphTooltip } from './MultiModeGraphTooltip';
|
||||
import { GraphDimensions } from './types';
|
||||
|
||||
export const GraphTooltip: React.FC<TooltipContentProps<GraphDimensions>> = ({
|
||||
export const GraphTooltip: React.FC<VizTooltipContentProps<GraphDimensions>> = ({
|
||||
mode = 'single',
|
||||
dimensions,
|
||||
activeDimensions,
|
||||
|
@ -3,7 +3,7 @@ import { mount } from 'enzyme';
|
||||
import { MultiModeGraphTooltip } from './MultiModeGraphTooltip';
|
||||
import { createDimension, ArrayVector, FieldType } from '@grafana/data';
|
||||
import { GraphDimensions } from './types';
|
||||
import { ActiveDimensions } from '../../Chart/Tooltip';
|
||||
import { ActiveDimensions } from '../../VizTooltip';
|
||||
|
||||
let dimensions: GraphDimensions;
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
import React from 'react';
|
||||
import { SeriesTable } from './SeriesTable';
|
||||
import { SeriesTable } from '../../VizTooltip';
|
||||
import { GraphTooltipContentProps } from './types';
|
||||
import { getMultiSeriesGraphHoverInfo } from '../utils';
|
||||
import { FlotPosition } from '../types';
|
||||
|
@ -6,7 +6,7 @@ import {
|
||||
getDisplayProcessor,
|
||||
getFieldDisplayName,
|
||||
} from '@grafana/data';
|
||||
import { SeriesTable } from './SeriesTable';
|
||||
import { SeriesTable } from '../../VizTooltip';
|
||||
import { GraphTooltipContentProps } from './types';
|
||||
|
||||
export const SingleModeGraphTooltip: React.FC<GraphTooltipContentProps> = ({
|
||||
|
@ -1,11 +0,0 @@
|
||||
package grafanaschema
|
||||
|
||||
|
||||
// TODO Relative imports are flatly disallowed by CUE, but that's what's
|
||||
// currently done in the corresponding typescript code. We'll have to make
|
||||
// cuetsy handle this with import mappings.
|
||||
import tooltip "github.com/grafana/grafana/packages/grafana-ui/src/components/Chart:grafanaschema"
|
||||
|
||||
GraphTooltipOptions: {
|
||||
mode: tooltip.TooltipMode
|
||||
} @cuetsy(targetType="interface")
|
@ -1,10 +1,5 @@
|
||||
import { ActiveDimensions } from '../../Chart/Tooltip';
|
||||
import { ActiveDimensions } from '../../VizTooltip';
|
||||
import { Dimension, Dimensions, TimeZone } from '@grafana/data';
|
||||
import { TooltipMode } from '../../Chart/models.gen';
|
||||
|
||||
export interface GraphTooltipOptions {
|
||||
mode: TooltipMode;
|
||||
}
|
||||
|
||||
export interface GraphDimensions extends Dimensions {
|
||||
xAxis: Dimension<number>;
|
||||
|
@ -2,11 +2,11 @@ import React from 'react';
|
||||
|
||||
import { withCenteredStory } from '../../utils/storybook/withCenteredStory';
|
||||
import { InfoTooltip } from './InfoTooltip';
|
||||
import { Tooltip } from '../Chart/Tooltip';
|
||||
import { VizTooltip } from '../VizTooltip';
|
||||
|
||||
export default {
|
||||
title: 'Overlays/TooltipInternal',
|
||||
component: Tooltip,
|
||||
component: VizTooltip,
|
||||
decorators: [withCenteredStory],
|
||||
};
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
import React from 'react';
|
||||
import { select, number, boolean } from '@storybook/addon-knobs';
|
||||
import { PieChart, PieChartType } from '@grafana/ui';
|
||||
import { PieChart, PieChartType, TooltipDisplayMode } from '@grafana/ui';
|
||||
import { withCenteredStory } from '../../utils/storybook/withCenteredStory';
|
||||
import {
|
||||
FieldColorModeId,
|
||||
@ -52,15 +52,16 @@ const getKnobs = () => {
|
||||
return {
|
||||
width: number('Width', 500),
|
||||
height: number('Height', 500),
|
||||
pieType: select('pieType', [PieChartType.Pie, PieChartType.Donut], PieChartType.Pie),
|
||||
pieType: select('pieType', Object.values(PieChartType), PieChartType.Pie),
|
||||
showLabelName: boolean('Label.showName', true),
|
||||
showLabelValue: boolean('Label.showValue', false),
|
||||
showLabelPercent: boolean('Label.showPercent', false),
|
||||
tooltipMode: select('Tooltip mode', Object.values(TooltipDisplayMode), TooltipDisplayMode.Single),
|
||||
};
|
||||
};
|
||||
|
||||
export const basic = () => {
|
||||
const { pieType, width, height } = getKnobs();
|
||||
const { pieType, width, height, tooltipMode } = getKnobs();
|
||||
|
||||
return (
|
||||
<PieChart
|
||||
@ -71,12 +72,13 @@ export const basic = () => {
|
||||
fieldConfig={fieldConfig}
|
||||
data={datapoints}
|
||||
pieType={pieType}
|
||||
tooltipOptions={{ mode: tooltipMode }}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export const donut = () => {
|
||||
const { width, height } = getKnobs();
|
||||
const { width, height, tooltipMode } = getKnobs();
|
||||
|
||||
return (
|
||||
<PieChart
|
||||
@ -87,6 +89,7 @@ export const donut = () => {
|
||||
fieldConfig={fieldConfig}
|
||||
data={datapoints}
|
||||
pieType={PieChartType.Donut}
|
||||
tooltipOptions={{ mode: tooltipMode }}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
@ -1,6 +1,5 @@
|
||||
import React, { FC, ReactNode } from 'react';
|
||||
import {
|
||||
DisplayValue,
|
||||
FALLBACK_COLOR,
|
||||
FieldDisplay,
|
||||
formattedValueToString,
|
||||
@ -29,6 +28,8 @@ import {
|
||||
PieChartSvgProps,
|
||||
PieChartType,
|
||||
} from './types';
|
||||
import { getTooltipContainerStyles } from '../../themes/mixins';
|
||||
import { SeriesTable, SeriesTableRowProps, VizTooltipOptions } from '../VizTooltip';
|
||||
|
||||
const defaultLegendOptions: PieChartLegendOptions = {
|
||||
displayMode: LegendDisplayMode.List,
|
||||
@ -44,6 +45,7 @@ export const PieChart: FC<PieChartProps> = ({
|
||||
fieldConfig,
|
||||
replaceVariables,
|
||||
legendOptions = defaultLegendOptions,
|
||||
tooltipOptions,
|
||||
onSeriesColorChange,
|
||||
width,
|
||||
height,
|
||||
@ -112,7 +114,13 @@ export const PieChart: FC<PieChartProps> = ({
|
||||
<VizLayout width={width} height={height} legend={getLegend(fieldDisplayValues, legendOptions)}>
|
||||
{(vizWidth: number, vizHeight: number) => {
|
||||
return (
|
||||
<PieChartSvg width={vizWidth} height={vizHeight} fieldDisplayValues={fieldDisplayValues} {...restProps} />
|
||||
<PieChartSvg
|
||||
width={vizWidth}
|
||||
height={vizHeight}
|
||||
fieldDisplayValues={fieldDisplayValues}
|
||||
tooltipOptions={tooltipOptions}
|
||||
{...restProps}
|
||||
/>
|
||||
);
|
||||
}}
|
||||
</VizLayout>
|
||||
@ -126,11 +134,12 @@ export const PieChartSvg: FC<PieChartSvgProps> = ({
|
||||
height,
|
||||
useGradients = true,
|
||||
displayLabels = [],
|
||||
tooltipOptions,
|
||||
}) => {
|
||||
const theme = useTheme();
|
||||
const componentInstanceId = useComponentInstanceId('PieChart');
|
||||
const styles = useStyles(getStyles);
|
||||
const tooltip = useTooltip<DisplayValue>();
|
||||
const tooltip = useTooltip<SeriesTableRowProps[]>();
|
||||
const { containerRef, TooltipInPortal } = useTooltipInPortal({
|
||||
detectBounds: true,
|
||||
scroll: true,
|
||||
@ -147,6 +156,7 @@ export const PieChartSvg: FC<PieChartSvgProps> = ({
|
||||
};
|
||||
|
||||
const showLabel = displayLabels.length > 0;
|
||||
const showTooltip = tooltipOptions.mode !== 'none' && tooltip.tooltipOpen;
|
||||
const total = fieldDisplayValues.reduce((acc, item) => item.display.numeric + acc, 0);
|
||||
const layout = getPieLayout(width, height, pieType);
|
||||
const colors = [
|
||||
@ -204,6 +214,7 @@ export const PieChartSvg: FC<PieChartSvgProps> = ({
|
||||
pie={pie}
|
||||
fill={getGradientColor(color)}
|
||||
openMenu={api.openMenu}
|
||||
tooltipOptions={tooltipOptions}
|
||||
>
|
||||
{label}
|
||||
</PieSlice>
|
||||
@ -212,7 +223,14 @@ export const PieChartSvg: FC<PieChartSvgProps> = ({
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<PieSlice key={arc.index} tooltip={tooltip} arc={arc} pie={pie} fill={getGradientColor(color)}>
|
||||
<PieSlice
|
||||
key={arc.index}
|
||||
tooltip={tooltip}
|
||||
arc={arc}
|
||||
pie={pie}
|
||||
fill={getGradientColor(color)}
|
||||
tooltipOptions={tooltipOptions}
|
||||
>
|
||||
{label}
|
||||
</PieSlice>
|
||||
);
|
||||
@ -222,16 +240,18 @@ export const PieChartSvg: FC<PieChartSvgProps> = ({
|
||||
</Pie>
|
||||
</Group>
|
||||
</svg>
|
||||
{tooltip.tooltipOpen && (
|
||||
{showTooltip ? (
|
||||
<TooltipInPortal
|
||||
key={Math.random()}
|
||||
top={tooltip.tooltipTop}
|
||||
className={styles.tooltipPortal}
|
||||
left={tooltip.tooltipLeft}
|
||||
unstyled={true}
|
||||
applyPositionStyle={true}
|
||||
>
|
||||
{tooltip.tooltipData!.title} {formattedValueToString(tooltip.tooltipData!)}
|
||||
<SeriesTable series={tooltip.tooltipData!} />
|
||||
</TooltipInPortal>
|
||||
)}
|
||||
) : null}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@ -241,18 +261,19 @@ const PieSlice: FC<{
|
||||
arc: PieArcDatum<FieldDisplay>;
|
||||
pie: ProvidedProps<FieldDisplay>;
|
||||
fill: string;
|
||||
tooltip: UseTooltipParams<DisplayValue>;
|
||||
tooltip: UseTooltipParams<SeriesTableRowProps[]>;
|
||||
tooltipOptions: VizTooltipOptions;
|
||||
openMenu?: (event: React.MouseEvent<SVGElement>) => void;
|
||||
}> = ({ arc, children, pie, openMenu, fill, tooltip }) => {
|
||||
}> = ({ arc, children, pie, openMenu, fill, tooltip, tooltipOptions }) => {
|
||||
const theme = useTheme();
|
||||
const styles = useStyles(getStyles);
|
||||
|
||||
const onMouseMoveOverArc = (event: any, datum: any) => {
|
||||
const onMouseMoveOverArc = (event: any) => {
|
||||
const coords = localPoint(event.target.ownerSVGElement, event);
|
||||
tooltip.showTooltip({
|
||||
tooltipLeft: coords!.x,
|
||||
tooltipTop: coords!.y,
|
||||
tooltipData: datum,
|
||||
tooltipData: getTooltipData(pie, arc, tooltipOptions),
|
||||
});
|
||||
};
|
||||
|
||||
@ -260,7 +281,7 @@ const PieSlice: FC<{
|
||||
<g
|
||||
key={arc.data.display.title}
|
||||
className={styles.svgArg}
|
||||
onMouseMove={(event) => onMouseMoveOverArc(event, arc.data.display)}
|
||||
onMouseMove={tooltipOptions.mode !== 'none' ? onMouseMoveOverArc : undefined}
|
||||
onMouseOut={tooltip.hideTooltip}
|
||||
onClick={openMenu}
|
||||
>
|
||||
@ -321,6 +342,30 @@ const PieLabel: FC<{
|
||||
);
|
||||
};
|
||||
|
||||
function getTooltipData(
|
||||
pie: ProvidedProps<FieldDisplay>,
|
||||
arc: PieArcDatum<FieldDisplay>,
|
||||
tooltipOptions: VizTooltipOptions
|
||||
) {
|
||||
if (tooltipOptions.mode === 'multi') {
|
||||
return pie.arcs.map((pieArc) => {
|
||||
return {
|
||||
color: pieArc.data.display.color ?? FALLBACK_COLOR,
|
||||
label: pieArc.data.display.title,
|
||||
value: formattedValueToString(pieArc.data.display),
|
||||
isActive: pieArc.index === arc.index,
|
||||
};
|
||||
});
|
||||
}
|
||||
return [
|
||||
{
|
||||
color: arc.data.display.color ?? FALLBACK_COLOR,
|
||||
label: arc.data.display.title,
|
||||
value: formattedValueToString(arc.data.display),
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
function getLabelPos(arc: PieArcDatum<FieldDisplay>, outerRadius: number, innerRadius: number) {
|
||||
const r = (outerRadius + innerRadius) / 2;
|
||||
const a = (+arc.startAngle + +arc.endAngle) / 2 - Math.PI / 2;
|
||||
@ -382,7 +427,7 @@ const getStyles = (theme: GrafanaTheme) => {
|
||||
}
|
||||
`,
|
||||
tooltipPortal: css`
|
||||
z-index: 1050;
|
||||
${getTooltipContainerStyles(theme)}
|
||||
`,
|
||||
};
|
||||
};
|
||||
|
@ -1,4 +1,5 @@
|
||||
import { DataFrame, FieldConfigSource, FieldDisplay, InterpolateFunction, ReduceDataOptions } from '@grafana/data';
|
||||
import { VizTooltipOptions } from '../VizTooltip';
|
||||
import { VizLegendOptions } from '..';
|
||||
|
||||
export interface PieChartSvgProps {
|
||||
@ -9,10 +10,12 @@ export interface PieChartSvgProps {
|
||||
displayLabels?: PieChartLabels[];
|
||||
useGradients?: boolean;
|
||||
onSeriesColorChange?: (label: string, color: string) => void;
|
||||
tooltipOptions: VizTooltipOptions;
|
||||
}
|
||||
|
||||
export interface PieChartProps extends Omit<PieChartSvgProps, 'fieldDisplayValues'> {
|
||||
legendOptions?: PieChartLegendOptions;
|
||||
tooltipOptions: VizTooltipOptions;
|
||||
reduceOptions: ReduceDataOptions;
|
||||
fieldConfig: FieldConfigSource<any>;
|
||||
replaceVariables: InterpolateFunction;
|
||||
|
@ -0,0 +1,61 @@
|
||||
import React from 'react';
|
||||
import { Story, Meta } from '@storybook/react';
|
||||
import { SeriesTable, SeriesTableProps } from './SeriesTable';
|
||||
|
||||
export default {
|
||||
title: 'VizTooltip/SeriesTable',
|
||||
component: SeriesTable,
|
||||
argTypes: {
|
||||
timestamp: {
|
||||
control: 'date',
|
||||
},
|
||||
},
|
||||
} as Meta;
|
||||
|
||||
const Template: Story<SeriesTableProps> = (args) => {
|
||||
const date = new Date(args.timestamp!).toLocaleString();
|
||||
return (
|
||||
<div>
|
||||
<SeriesTable {...args} timestamp={date} />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export const basic = Template.bind({});
|
||||
|
||||
basic.args = {
|
||||
timestamp: new Date('2021-01-01T00:00:00').toISOString(),
|
||||
series: [
|
||||
{
|
||||
color: '#299c46',
|
||||
label: 'label 1',
|
||||
value: '100 W',
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
export const multi = Template.bind({});
|
||||
|
||||
multi.args = {
|
||||
timestamp: new Date('2021-01-01T00:00:00').toISOString(),
|
||||
series: [
|
||||
{
|
||||
color: '#299c46',
|
||||
label: 'label 1',
|
||||
value: '100 W',
|
||||
isActive: false,
|
||||
},
|
||||
{
|
||||
color: '#9933cc',
|
||||
label: 'label yes',
|
||||
value: '25 W',
|
||||
isActive: true,
|
||||
},
|
||||
{
|
||||
color: '#eb7b18',
|
||||
label: 'label 3',
|
||||
value: '150 W',
|
||||
isActive: false,
|
||||
},
|
||||
],
|
||||
};
|
@ -1,10 +1,12 @@
|
||||
import React from 'react';
|
||||
import { stylesFactory } from '../../../themes/stylesFactory';
|
||||
import { GrafanaTheme, GraphSeriesValue } from '@grafana/data';
|
||||
import { css, cx } from '@emotion/css';
|
||||
import { SeriesIcon } from '../../VizLegend/SeriesIcon';
|
||||
import { useTheme } from '../../../themes';
|
||||
import { SeriesIcon } from '../VizLegend/SeriesIcon';
|
||||
import { useStyles } from '../../themes';
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export interface SeriesTableRowProps {
|
||||
color?: string;
|
||||
label?: string;
|
||||
@ -12,10 +14,11 @@ export interface SeriesTableRowProps {
|
||||
isActive?: boolean;
|
||||
}
|
||||
|
||||
const getSeriesTableRowStyles = stylesFactory((theme: GrafanaTheme) => {
|
||||
const getSeriesTableRowStyles = (theme: GrafanaTheme) => {
|
||||
return {
|
||||
icon: css`
|
||||
margin-right: ${theme.spacing.xs};
|
||||
vertical-align: middle;
|
||||
`,
|
||||
seriesTable: css`
|
||||
display: table;
|
||||
@ -41,11 +44,11 @@ const getSeriesTableRowStyles = stylesFactory((theme: GrafanaTheme) => {
|
||||
font-size: ${theme.typography.size.sm};
|
||||
`,
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
const SeriesTableRow: React.FC<SeriesTableRowProps> = ({ color, label, value, isActive }) => {
|
||||
const theme = useTheme();
|
||||
const styles = getSeriesTableRowStyles(theme);
|
||||
const styles = useStyles(getSeriesTableRowStyles);
|
||||
|
||||
return (
|
||||
<div className={cx(styles.seriesTableRow, isActive && styles.activeSeries)}>
|
||||
{color && (
|
||||
@ -59,14 +62,19 @@ const SeriesTableRow: React.FC<SeriesTableRowProps> = ({ color, label, value, is
|
||||
);
|
||||
};
|
||||
|
||||
interface SeriesTableProps {
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export interface SeriesTableProps {
|
||||
timestamp?: string | GraphSeriesValue;
|
||||
series: SeriesTableRowProps[];
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export const SeriesTable: React.FC<SeriesTableProps> = ({ timestamp, series }) => {
|
||||
const theme = useTheme();
|
||||
const styles = getSeriesTableRowStyles(theme);
|
||||
const styles = useStyles(getSeriesTableRowStyles);
|
||||
|
||||
return (
|
||||
<>
|
@ -3,9 +3,9 @@ import { css } from '@emotion/css';
|
||||
import { Portal } from '../Portal/Portal';
|
||||
import { Dimensions, TimeZone } from '@grafana/data';
|
||||
import { FlotPosition } from '../Graph/types';
|
||||
import { TooltipContainer } from './TooltipContainer';
|
||||
import { VizTooltipContainer } from './VizTooltipContainer';
|
||||
import { useStyles } from '../../themes';
|
||||
import { TooltipMode } from './models.gen';
|
||||
import { TooltipDisplayMode } from './models.gen';
|
||||
|
||||
// Describes active dimensions user interacts with
|
||||
// It's a key-value pair where:
|
||||
@ -14,7 +14,7 @@ import { TooltipMode } from './models.gen';
|
||||
// If row is undefined, it means that we are not hovering over a datapoint
|
||||
export type ActiveDimensions<T extends Dimensions = any> = { [key in keyof T]: [number, number | undefined] | null };
|
||||
|
||||
export interface TooltipContentProps<T extends Dimensions = any> {
|
||||
export interface VizTooltipContentProps<T extends Dimensions = any> {
|
||||
// Each dimension is described by array of fields representing it
|
||||
// I.e. for graph there are two dimensions: x and y axis:
|
||||
// { xAxis: [<array of time fields>], yAxis: [<array of value fields>]}
|
||||
@ -23,15 +23,15 @@ export interface TooltipContentProps<T extends Dimensions = any> {
|
||||
activeDimensions?: ActiveDimensions<T>;
|
||||
timeZone?: TimeZone;
|
||||
pos: FlotPosition;
|
||||
mode: TooltipMode;
|
||||
mode: TooltipDisplayMode;
|
||||
}
|
||||
|
||||
export interface TooltipProps {
|
||||
export interface VizTooltipProps {
|
||||
/** Element used as tooltips content */
|
||||
content?: React.ReactElement<any>;
|
||||
|
||||
/** Optional component to be used as a tooltip content */
|
||||
tooltipComponent?: React.ComponentType<TooltipContentProps>;
|
||||
tooltipComponent?: React.ComponentType<VizTooltipContentProps>;
|
||||
|
||||
/** x/y position relative to the window */
|
||||
position?: { x: number; y: number };
|
||||
@ -42,24 +42,27 @@ export interface TooltipProps {
|
||||
// Mode in which tooltip works
|
||||
// - single - display single series info
|
||||
// - multi - display all series info
|
||||
mode?: TooltipMode;
|
||||
mode?: TooltipDisplayMode;
|
||||
}
|
||||
|
||||
export const Tooltip: React.FC<TooltipProps> = ({ content, position, offset }) => {
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export const VizTooltip: React.FC<VizTooltipProps> = ({ content, position, offset }) => {
|
||||
const styles = useStyles(getStyles);
|
||||
if (position) {
|
||||
return (
|
||||
<Portal className={styles.portal}>
|
||||
<TooltipContainer position={position} offset={offset || { x: 0, y: 0 }}>
|
||||
<VizTooltipContainer position={position} offset={offset || { x: 0, y: 0 }}>
|
||||
{content}
|
||||
</TooltipContainer>
|
||||
</VizTooltipContainer>
|
||||
</Portal>
|
||||
);
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
Tooltip.displayName = 'ChartTooltip';
|
||||
VizTooltip.displayName = 'VizTooltip';
|
||||
|
||||
const getStyles = () => {
|
||||
return {
|
@ -1,24 +1,29 @@
|
||||
import React, { useState, useLayoutEffect, useRef, HTMLAttributes, useMemo } from 'react';
|
||||
import { stylesFactory } from '../../themes/stylesFactory';
|
||||
import { css, cx } from '@emotion/css';
|
||||
import { useTheme } from '../../themes/ThemeContext';
|
||||
import { useStyles } from '../../themes';
|
||||
import { getTooltipContainerStyles } from '../../themes/mixins';
|
||||
import useWindowSize from 'react-use/lib/useWindowSize';
|
||||
import { Dimensions2D, GrafanaTheme } from '@grafana/data';
|
||||
|
||||
interface TooltipContainerProps extends HTMLAttributes<HTMLDivElement> {
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export interface VizTooltipContainerProps extends HTMLAttributes<HTMLDivElement> {
|
||||
position: { x: number; y: number };
|
||||
offset: { x: number; y: number };
|
||||
children?: JSX.Element;
|
||||
}
|
||||
|
||||
export const TooltipContainer: React.FC<TooltipContainerProps> = ({
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export const VizTooltipContainer: React.FC<VizTooltipContainerProps> = ({
|
||||
position: { x: positionX, y: positionY },
|
||||
offset: { x: offsetX, y: offsetY },
|
||||
children,
|
||||
className,
|
||||
...otherProps
|
||||
}) => {
|
||||
const theme = useTheme();
|
||||
const tooltipRef = useRef<HTMLDivElement>(null);
|
||||
const tooltipMeasurementRef = useRef<Dimensions2D>({ width: 0, height: 0 });
|
||||
const { width, height } = useWindowSize();
|
||||
@ -80,7 +85,7 @@ export const TooltipContainer: React.FC<TooltipContainerProps> = ({
|
||||
});
|
||||
}, [width, height, positionX, offsetX, positionY, offsetY]);
|
||||
|
||||
const styles = getTooltipContainerStyles(theme);
|
||||
const styles = useStyles(getStyles);
|
||||
|
||||
return (
|
||||
<div
|
||||
@ -99,18 +104,10 @@ export const TooltipContainer: React.FC<TooltipContainerProps> = ({
|
||||
);
|
||||
};
|
||||
|
||||
TooltipContainer.displayName = 'TooltipContainer';
|
||||
VizTooltipContainer.displayName = 'VizTooltipContainer';
|
||||
|
||||
const getTooltipContainerStyles = stylesFactory((theme: GrafanaTheme) => {
|
||||
return {
|
||||
wrapper: css`
|
||||
overflow: hidden;
|
||||
background: ${theme.colors.bg2};
|
||||
/* max-width is set up based on .grafana-tooltip class that's used in dashboard */
|
||||
max-width: 800px;
|
||||
padding: ${theme.spacing.sm};
|
||||
border-radius: ${theme.border.radius.sm};
|
||||
z-index: ${theme.zIndex.tooltip};
|
||||
`,
|
||||
};
|
||||
const getStyles = (theme: GrafanaTheme) => ({
|
||||
wrapper: css`
|
||||
${getTooltipContainerStyles(theme)}
|
||||
`,
|
||||
});
|
4
packages/grafana-ui/src/components/VizTooltip/index.tsx
Normal file
4
packages/grafana-ui/src/components/VizTooltip/index.tsx
Normal file
@ -0,0 +1,4 @@
|
||||
export { VizTooltip, VizTooltipContentProps, VizTooltipProps, ActiveDimensions } from './VizTooltip';
|
||||
export { VizTooltipContainer, VizTooltipContainerProps } from './VizTooltipContainer';
|
||||
export { SeriesTable, SeriesTableProps, SeriesTableRowProps } from './SeriesTable';
|
||||
export { TooltipDisplayMode, VizTooltipOptions } from './models.gen';
|
7
packages/grafana-ui/src/components/VizTooltip/models.cue
Normal file
7
packages/grafana-ui/src/components/VizTooltip/models.cue
Normal file
@ -0,0 +1,7 @@
|
||||
package grafanaschema
|
||||
|
||||
TooltipDisplayMode: "single" | "multi" | "none" @cuetsy(targetType="enum")
|
||||
|
||||
VizTooltipOptions: {
|
||||
mode: TooltipDisplayMode
|
||||
} @cuetsy(targetType="interface")
|
@ -3,8 +3,18 @@
|
||||
// It is currenty hand written but will serve as the target for cuetsy
|
||||
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
import { TooltipMode } from '../../Chart/models.gen';
|
||||
|
||||
export interface GraphTooltipOptions {
|
||||
mode: TooltipMode;
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export enum TooltipDisplayMode {
|
||||
Single = 'single',
|
||||
Multi = 'multi',
|
||||
None = 'none',
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export type VizTooltipOptions = {
|
||||
mode: TooltipDisplayMode;
|
||||
};
|
@ -74,7 +74,15 @@ export { Graph } from './Graph/Graph';
|
||||
export { GraphWithLegend } from './Graph/GraphWithLegend';
|
||||
export { GraphContextMenu, GraphContextMenuHeader } from './Graph/GraphContextMenu';
|
||||
export { BarGauge, BarGaugeDisplayMode } from './BarGauge/BarGauge';
|
||||
export { GraphTooltipOptions } from './Graph/GraphTooltip/types';
|
||||
export {
|
||||
VizTooltip,
|
||||
VizTooltipContainer,
|
||||
SeriesTable,
|
||||
VizTooltipOptions,
|
||||
TooltipDisplayMode,
|
||||
SeriesTableProps,
|
||||
SeriesTableRowProps,
|
||||
} from './VizTooltip';
|
||||
export { VizRepeater, VizRepeaterRenderValueProps } from './VizRepeater/VizRepeater';
|
||||
export { graphTimeFormat, graphTickFormatter } from './Graph/utils';
|
||||
export {
|
||||
@ -132,8 +140,6 @@ export { Spinner } from './Spinner/Spinner';
|
||||
export { FadeTransition } from './transitions/FadeTransition';
|
||||
export { SlideOutTransition } from './transitions/SlideOutTransition';
|
||||
export { Segment, SegmentAsync, SegmentInput, SegmentSelect } from './Segment/';
|
||||
export { default as Chart } from './Chart';
|
||||
export { TooltipContainer } from './Chart/TooltipContainer';
|
||||
export { Drawer } from './Drawer/Drawer';
|
||||
export { Slider } from './Slider/Slider';
|
||||
export { RangeSlider } from './Slider/RangeSlider';
|
||||
|
@ -2,7 +2,7 @@ import React from 'react';
|
||||
import { Portal } from '../../Portal/Portal';
|
||||
import { usePlotContext } from '../context';
|
||||
import { CursorPlugin } from './CursorPlugin';
|
||||
import { SeriesTable, SeriesTableRowProps } from '../../Graph/GraphTooltip/SeriesTable';
|
||||
import { VizTooltipContainer, SeriesTable, SeriesTableRowProps, TooltipDisplayMode } from '../../VizTooltip';
|
||||
import {
|
||||
DataFrame,
|
||||
FieldType,
|
||||
@ -11,12 +11,10 @@ import {
|
||||
getFieldDisplayName,
|
||||
TimeZone,
|
||||
} from '@grafana/data';
|
||||
import { TooltipContainer } from '../../Chart/TooltipContainer';
|
||||
import { TooltipMode } from '../../Chart/models.gen';
|
||||
import { useGraphNGContext } from '../../GraphNG/hooks';
|
||||
|
||||
interface TooltipPluginProps {
|
||||
mode?: TooltipMode;
|
||||
mode?: TooltipDisplayMode;
|
||||
timeZone: TimeZone;
|
||||
data: DataFrame[];
|
||||
}
|
||||
@ -121,9 +119,9 @@ export const TooltipPlugin: React.FC<TooltipPluginProps> = ({ mode = 'single', t
|
||||
|
||||
return (
|
||||
<Portal>
|
||||
<TooltipContainer position={{ x: coords.viewport.x, y: coords.viewport.y }} offset={{ x: 10, y: 10 }}>
|
||||
<VizTooltipContainer position={{ x: coords.viewport.x, y: coords.viewport.y }} offset={{ x: 10, y: 10 }}>
|
||||
{tooltip}
|
||||
</TooltipContainer>
|
||||
</VizTooltipContainer>
|
||||
</Portal>
|
||||
);
|
||||
}}
|
||||
|
@ -4,13 +4,13 @@ import tinycolor from 'tinycolor2';
|
||||
|
||||
export function cardChrome(theme: GrafanaTheme): string {
|
||||
return `
|
||||
background: ${theme.colors.bg2};
|
||||
&:hover {
|
||||
background: ${hoverColor(theme.colors.bg2, theme)};
|
||||
}
|
||||
box-shadow: ${theme.shadows.listItem};
|
||||
border-radius: ${theme.border.radius.md};
|
||||
`;
|
||||
background: ${theme.colors.bg2};
|
||||
&:hover {
|
||||
background: ${hoverColor(theme.colors.bg2, theme)};
|
||||
}
|
||||
box-shadow: ${theme.shadows.listItem};
|
||||
border-radius: ${theme.border.radius.md};
|
||||
`;
|
||||
}
|
||||
|
||||
export function hoverColor(color: string, theme: GrafanaTheme): string {
|
||||
@ -30,9 +30,9 @@ export function listItem(theme: GrafanaTheme): string {
|
||||
|
||||
export function listItemSelected(theme: GrafanaTheme): string {
|
||||
return `
|
||||
background: ${hoverColor(theme.colors.bg2, theme)};
|
||||
color: ${theme.colors.textStrong};
|
||||
`;
|
||||
background: ${hoverColor(theme.colors.bg2, theme)};
|
||||
color: ${theme.colors.textStrong};
|
||||
`;
|
||||
}
|
||||
|
||||
export function mediaUp(breakpoint: string) {
|
||||
@ -61,3 +61,13 @@ export function getFocusStyles(theme: GrafanaThemeV2): CSSObject {
|
||||
transition: `all 0.2s cubic-bezier(0.19, 1, 0.22, 1)`,
|
||||
};
|
||||
}
|
||||
|
||||
// max-width is set up based on .grafana-tooltip class that's used in dashboard
|
||||
export const getTooltipContainerStyles = (theme: GrafanaTheme) => `
|
||||
overflow: hidden;
|
||||
background: ${theme.colors.bg2};
|
||||
max-width: 800px;
|
||||
padding: ${theme.spacing.sm};
|
||||
border-radius: ${theme.border.radius.sm};
|
||||
z-index: ${theme.zIndex.tooltip};
|
||||
`;
|
||||
|
@ -21,6 +21,7 @@ import {
|
||||
useStyles,
|
||||
useTheme,
|
||||
ZoomPlugin,
|
||||
TooltipDisplayMode,
|
||||
} from '@grafana/ui';
|
||||
import { defaultGraphConfig, getGraphFieldConfig } from 'app/plugins/panel/timeseries/config';
|
||||
import { hideSeriesConfigFactory } from 'app/plugins/panel/timeseries/overrides/hideSeriesConfigFactory';
|
||||
@ -129,7 +130,7 @@ export function ExploreGraphNGPanel({
|
||||
timeZone={timeZone}
|
||||
>
|
||||
<ZoomPlugin onZoom={onUpdateTimeRange} />
|
||||
<TooltipPlugin data={data} mode="single" timeZone={timeZone} />
|
||||
<TooltipPlugin data={data} mode={TooltipDisplayMode.Single} timeZone={timeZone} />
|
||||
<ContextMenuPlugin data={data} timeZone={timeZone} />
|
||||
{annotations && <ExemplarsPlugin exemplars={annotations} timeZone={timeZone} getFieldLinks={getFieldLinks} />}
|
||||
</GraphNG>
|
||||
|
@ -11,8 +11,9 @@ import {
|
||||
Collapse,
|
||||
GraphSeriesToggler,
|
||||
GraphSeriesTogglerAPI,
|
||||
Chart,
|
||||
VizTooltip,
|
||||
Icon,
|
||||
TooltipDisplayMode,
|
||||
} from '@grafana/ui';
|
||||
|
||||
const MAX_NUMBER_OF_TIME_SERIES = 20;
|
||||
@ -130,7 +131,7 @@ class UnThemedExploreGraphPanel extends PureComponent<Props, State> {
|
||||
onHorizontalRegionSelected={this.onChangeTime}
|
||||
>
|
||||
{/* For logs we are using mulit mode until we refactor logs histogram to use barWidth instead of lineWidth to render bars */}
|
||||
<Chart.Tooltip mode={showBars ? 'multi' : 'single'} />
|
||||
<VizTooltip mode={showBars ? TooltipDisplayMode.Multi : TooltipDisplayMode.Single} />
|
||||
</GraphWithLegend>
|
||||
);
|
||||
}}
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { GraphTooltipOptions, LegendDisplayMode, LegendPlacement } from '@grafana/ui';
|
||||
import { VizTooltipOptions, TooltipDisplayMode, LegendDisplayMode, LegendPlacement } from '@grafana/ui';
|
||||
import { YAxis } from '@grafana/data';
|
||||
|
||||
export interface SeriesOptions {
|
||||
@ -21,7 +21,7 @@ export interface Options {
|
||||
series: {
|
||||
[alias: string]: SeriesOptions;
|
||||
};
|
||||
tooltipOptions: GraphTooltipOptions;
|
||||
tooltipOptions: VizTooltipOptions;
|
||||
}
|
||||
|
||||
export const defaults: Options = {
|
||||
@ -35,7 +35,7 @@ export const defaults: Options = {
|
||||
placement: 'bottom',
|
||||
},
|
||||
series: {},
|
||||
tooltipOptions: { mode: 'single' },
|
||||
tooltipOptions: { mode: TooltipDisplayMode.Single },
|
||||
};
|
||||
|
||||
export interface GraphLegendEditorLegendOptions {
|
||||
|
@ -36,6 +36,7 @@ export const PieChartPanel: React.FC<Props> = ({
|
||||
pieType={options.pieType}
|
||||
displayLabels={options.displayLabels}
|
||||
legendOptions={options.legend}
|
||||
tooltipOptions={options.tooltip}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
@ -49,6 +49,19 @@ export const plugin = new PanelPlugin<PieChartOptions>(PieChartPanel)
|
||||
],
|
||||
},
|
||||
})
|
||||
.addRadio({
|
||||
name: 'Tooltip mode',
|
||||
path: 'tooltip.mode',
|
||||
description: '',
|
||||
defaultValue: 'single',
|
||||
settings: {
|
||||
options: [
|
||||
{ value: 'single', label: 'Single' },
|
||||
{ value: 'multi', label: 'All' },
|
||||
{ value: 'none', label: 'Hidden' },
|
||||
],
|
||||
},
|
||||
})
|
||||
.addRadio({
|
||||
path: 'legend.displayMode',
|
||||
name: 'Legend mode',
|
||||
|
@ -1,7 +1,13 @@
|
||||
import { PieChartType, SingleStatBaseOptions, PieChartLabels, PieChartLegendOptions } from '@grafana/ui';
|
||||
|
||||
import {
|
||||
PieChartType,
|
||||
SingleStatBaseOptions,
|
||||
PieChartLabels,
|
||||
PieChartLegendOptions,
|
||||
VizTooltipOptions,
|
||||
} from '@grafana/ui';
|
||||
export interface PieChartOptions extends SingleStatBaseOptions {
|
||||
pieType: PieChartType;
|
||||
displayLabels: PieChartLabels[];
|
||||
legend: PieChartLegendOptions;
|
||||
tooltip: VizTooltipOptions;
|
||||
}
|
||||
|
@ -20,6 +20,7 @@ import {
|
||||
LineStyle,
|
||||
PointVisibility,
|
||||
StackingMode,
|
||||
TooltipDisplayMode,
|
||||
} from '@grafana/ui';
|
||||
import { Options } from './types';
|
||||
import omitBy from 'lodash/omitBy';
|
||||
@ -291,7 +292,7 @@ export function flotToGraphOptions(angular: any): { fieldConfig: FieldConfigSour
|
||||
calcs: [],
|
||||
},
|
||||
tooltipOptions: {
|
||||
mode: 'single',
|
||||
mode: TooltipDisplayMode.Single,
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
import React, { useCallback, useRef, useState } from 'react';
|
||||
import { GrafanaTheme } from '@grafana/data';
|
||||
import { HorizontalGroup, Portal, Tag, TooltipContainer, useStyles } from '@grafana/ui';
|
||||
import { HorizontalGroup, Portal, Tag, VizTooltipContainer, useStyles } from '@grafana/ui';
|
||||
import { css } from '@emotion/css';
|
||||
|
||||
interface AnnotationMarkerProps {
|
||||
@ -38,7 +38,7 @@ export const AnnotationMarker: React.FC<AnnotationMarkerProps> = ({ time, text,
|
||||
const elBBox = el.getBoundingClientRect();
|
||||
|
||||
return (
|
||||
<TooltipContainer
|
||||
<VizTooltipContainer
|
||||
position={{ x: elBBox.left, y: elBBox.top + elBBox.height }}
|
||||
offset={{ x: 0, y: 0 }}
|
||||
onMouseEnter={onMouseEnter}
|
||||
@ -61,7 +61,7 @@ export const AnnotationMarker: React.FC<AnnotationMarkerProps> = ({ time, text,
|
||||
</>
|
||||
</div>
|
||||
</div>
|
||||
</TooltipContainer>
|
||||
</VizTooltipContainer>
|
||||
);
|
||||
}, [onMouseEnter, onMouseLeave, styles, time, text, tags]);
|
||||
|
||||
|
@ -8,7 +8,7 @@ import {
|
||||
systemDateFormats,
|
||||
TimeZone,
|
||||
} from '@grafana/data';
|
||||
import { FieldLinkList, Portal, TooltipContainer, useStyles } from '@grafana/ui';
|
||||
import { FieldLinkList, Portal, VizTooltipContainer, useStyles } from '@grafana/ui';
|
||||
import { css, cx } from '@emotion/css';
|
||||
import React, { useCallback, useRef, useState } from 'react';
|
||||
|
||||
@ -58,7 +58,7 @@ export const ExemplarMarker: React.FC<ExemplarMarkerProps> = ({ timeZone, dataFr
|
||||
const elBBox = el.getBoundingClientRect();
|
||||
|
||||
return (
|
||||
<TooltipContainer
|
||||
<VizTooltipContainer
|
||||
position={{ x: elBBox.left, y: elBBox.top + elBBox.height }}
|
||||
offset={{ x: 0, y: 0 }}
|
||||
onMouseEnter={onMouseEnter}
|
||||
@ -93,7 +93,7 @@ export const ExemplarMarker: React.FC<ExemplarMarkerProps> = ({ timeZone, dataFr
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</TooltipContainer>
|
||||
</VizTooltipContainer>
|
||||
);
|
||||
}, [dataFrame.fields, getFieldLinks, index, onMouseEnter, onMouseLeave, styles, timeFormatter]);
|
||||
|
||||
|
@ -1,9 +1,9 @@
|
||||
import { VizLegendOptions, GraphTooltipOptions } from '@grafana/ui';
|
||||
import { VizLegendOptions, VizTooltipOptions } from '@grafana/ui';
|
||||
|
||||
export interface OptionsWithLegend {
|
||||
legend: VizLegendOptions;
|
||||
}
|
||||
|
||||
export interface Options extends OptionsWithLegend {
|
||||
tooltipOptions: GraphTooltipOptions;
|
||||
tooltipOptions: VizTooltipOptions;
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { GraphTooltipOptions } from '@grafana/ui';
|
||||
import { VizTooltipOptions } from '@grafana/ui';
|
||||
import { OptionsWithLegend } from '../timeseries/types';
|
||||
|
||||
export interface XYDimensionConfig {
|
||||
@ -9,5 +9,5 @@ export interface XYDimensionConfig {
|
||||
|
||||
export interface Options extends OptionsWithLegend {
|
||||
dims: XYDimensionConfig;
|
||||
tooltipOptions: GraphTooltipOptions;
|
||||
tooltipOptions: VizTooltipOptions;
|
||||
}
|
||||
|
@ -46,13 +46,13 @@ cue def -s $(find packages/grafana-data -type f -name "*.cue") > cue/data/gen.cu
|
||||
#
|
||||
# It's important to understand why this is necessary, though. We are expecting
|
||||
# that these core components may depend on each other - e.g., how
|
||||
# GraphTooltipOptions composes in TooltipMode. We have to preserve those
|
||||
# VizTooltipOptions composes in TooltipDisplayMode. We have to preserve those
|
||||
# literal identifiers in our assembled CUE, so that when a panel plugin's
|
||||
# models.cue imports and references something like GraphTooltipOptions in CUE,
|
||||
# models.cue imports and references something like VizTooltipOptions in CUE,
|
||||
# it's still the same identifier as appeared in the original core models.cue
|
||||
# files, AND therefore is exactly the identifier that appears in
|
||||
# cuetsy-generated @grafana/{ui,data} packages. That is, as long as we preserve
|
||||
# the relation between the identifier "GraphTooltipOptions" as a top-level
|
||||
# the relation between the identifier "VizTooltipOptions" as a top-level
|
||||
# importable thing at all stages on the CUE side, then everything on the
|
||||
# TypeScript side will line up.
|
||||
sed -i -e 's/^import.*//g' {cue/ui/gen.cue,cue/data/gen.cue}
|
||||
|
Loading…
Reference in New Issue
Block a user