diff --git a/packages/grafana-data/src/utils/OptionsUIBuilders.ts b/packages/grafana-data/src/utils/OptionsUIBuilders.ts index cd62939c409..0c63d9f9292 100644 --- a/packages/grafana-data/src/utils/OptionsUIBuilders.ts +++ b/packages/grafana-data/src/utils/OptionsUIBuilders.ts @@ -186,7 +186,6 @@ export class PanelOptionsEditorBuilder extends OptionsUIRegistryBuilde ) { return this.addCustomEditor({ ...config, - defaultValue: config.defaultValue ?? [], id: config.path, editor: standardEditorsRegistry.get('multi-select').editor as any, }); diff --git a/packages/grafana-ui/src/components/DataLinks/DataLinksContextMenu.tsx b/packages/grafana-ui/src/components/DataLinks/DataLinksContextMenu.tsx index 7252dc94e85..14a300e92e4 100644 --- a/packages/grafana-ui/src/components/DataLinks/DataLinksContextMenu.tsx +++ b/packages/grafana-ui/src/components/DataLinks/DataLinksContextMenu.tsx @@ -12,7 +12,7 @@ interface DataLinksContextMenuProps { } export interface DataLinksContextMenuApi { - openMenu?: React.MouseEventHandler; + openMenu?: React.MouseEventHandler; targetClassName?: string; } diff --git a/packages/grafana-ui/src/components/PieChart/PieChart.story.tsx b/packages/grafana-ui/src/components/PieChart/PieChart.story.tsx index 41e9654d5d0..d021770d37c 100644 --- a/packages/grafana-ui/src/components/PieChart/PieChart.story.tsx +++ b/packages/grafana-ui/src/components/PieChart/PieChart.story.tsx @@ -2,6 +2,7 @@ import React from 'react'; import { object, select, number, boolean } from '@storybook/addon-knobs'; import { PieChart, PieChartType } from '@grafana/ui'; import { withCenteredStory } from '../../utils/storybook/withCenteredStory'; +import { FieldConfig } from '@grafana/data'; export default { title: 'Visualizations/PieChart', @@ -9,14 +10,25 @@ export default { component: PieChart, }; +const fieldConfig: FieldConfig = { + displayName: '', + min: 0, + max: 10, + decimals: 10, + thresholds: {} as any, + noValue: 'no value', + unit: 'km/s', + links: {} as any, +}; + const getKnobs = () => { return { datapoints: object('datapoints', [ - { numeric: 100, text: '100', title: 'USA' }, - { numeric: 200, text: '200', title: 'Canada' }, - { numeric: 20, text: '20', title: 'Sweden' }, - { numeric: 50, text: '50', title: 'Spain' }, - { numeric: 70, text: '70', title: 'Germeny' }, + { field: fieldConfig, hasLinks: false, name: 'USA', display: { numeric: 100, text: '100', title: 'USA' } }, + { field: fieldConfig, hasLinks: false, name: 'Canada', display: { numeric: 200, text: '200', title: 'Canada' } }, + { field: fieldConfig, hasLinks: false, name: 'Sweden', display: { numeric: 20, text: '20', title: 'Sweden' } }, + { field: fieldConfig, hasLinks: false, name: 'Spain', display: { numeric: 50, text: '50', title: 'Spain' } }, + { field: fieldConfig, hasLinks: false, name: 'Germany', display: { numeric: 70, text: '70', title: 'Germeny' } }, ]), width: number('Width', 500), height: number('Height', 500), @@ -30,11 +42,11 @@ const getKnobs = () => { export const basic = () => { const { datapoints, pieType, width, height } = getKnobs(); - return ; + return ; }; export const donut = () => { const { datapoints, width, height } = getKnobs(); - return ; + return ; }; diff --git a/packages/grafana-ui/src/components/PieChart/PieChart.tsx b/packages/grafana-ui/src/components/PieChart/PieChart.tsx index 63bca6fdb48..5a965fd5523 100644 --- a/packages/grafana-ui/src/components/PieChart/PieChart.tsx +++ b/packages/grafana-ui/src/components/PieChart/PieChart.tsx @@ -1,8 +1,8 @@ -import React, { FC } from 'react'; -import { DisplayValue, FALLBACK_COLOR, formattedValueToString, GrafanaTheme } from '@grafana/data'; +import React, { FC, ReactNode } from 'react'; +import { DisplayValue, FALLBACK_COLOR, FieldDisplay, formattedValueToString, GrafanaTheme } from '@grafana/data'; import { useStyles, useTheme } from '../../themes/ThemeContext'; import tinycolor from 'tinycolor2'; -import Pie, { PieArcDatum } from '@visx/shape/lib/shapes/Pie'; +import Pie, { PieArcDatum, ProvidedProps } from '@visx/shape/lib/shapes/Pie'; import { Group } from '@visx/group'; import { RadialGradient } from '@visx/gradient'; import { localPoint } from '@visx/event'; @@ -12,6 +12,8 @@ import { css } from 'emotion'; import { VizLegend, VizLegendItem } from '..'; import { VizLayout } from '../VizLayout/VizLayout'; import { LegendDisplayMode, VizLegendOptions } from '../VizLegend/types'; +import { DataLinksContextMenu } from '../DataLinks/DataLinksContextMenu'; +import { UseTooltipParams } from '@visx/tooltip/lib/hooks/useTooltip'; export enum PieChartLabels { Name = 'name', @@ -27,7 +29,7 @@ export enum PieChartLegendValues { interface SvgProps { height: number; width: number; - values: DisplayValue[]; + fieldDisplayValues: FieldDisplay[]; pieType: PieChartType; displayLabels?: PieChartLabels[]; useGradients?: boolean; @@ -54,24 +56,26 @@ const defaultLegendOptions: PieChartLegendOptions = { }; export const PieChart: FC = ({ - values, + fieldDisplayValues, legendOptions = defaultLegendOptions, onSeriesColorChange, width, height, ...restProps }) => { - const getLegend = (values: DisplayValue[], legendOptions: PieChartLegendOptions) => { + const getLegend = (fields: FieldDisplay[], legendOptions: PieChartLegendOptions) => { if (legendOptions.displayMode === LegendDisplayMode.Hidden) { return undefined; } + const values = fields.map((v) => v.display); const total = values.reduce((acc, item) => item.numeric + acc, 0); - const legendItems = values.map((value) => { + const legendItems = values.map((value, idx) => { return { label: value.title ?? '', color: value.color ?? FALLBACK_COLOR, yAxis: 1, + getItemKey: () => (value.title ?? '') + idx, getDisplayValues: () => { const valuesToShow = legendOptions.values ?? []; let displayValues = []; @@ -108,16 +112,18 @@ export const PieChart: FC = ({ }; return ( - + {(vizWidth: number, vizHeight: number) => { - return ; + return ( + + ); }} ); }; export const PieChartSvg: FC = ({ - values, + fieldDisplayValues, pieType, width, height, @@ -127,44 +133,37 @@ export const PieChartSvg: FC = ({ const theme = useTheme(); const componentInstanceId = useComponentInstanceId('PieChart'); const styles = useStyles(getStyles); - const { tooltipData, tooltipLeft, tooltipTop, tooltipOpen, showTooltip, hideTooltip } = useTooltip(); + const tooltip = useTooltip(); const { containerRef, TooltipInPortal } = useTooltipInPortal({ detectBounds: true, scroll: true, }); - if (values.length < 0) { + if (fieldDisplayValues.length < 0) { return
No data
; } - const getValue = (d: DisplayValue) => d.numeric; + const getValue = (d: FieldDisplay) => d.display.numeric; const getGradientId = (color: string) => `${componentInstanceId}-${color}`; const getGradientColor = (color: string) => { return `url(#${getGradientId(color)})`; }; - const onMouseMoveOverArc = (event: any, datum: any) => { - const coords = localPoint(event.target.ownerSVGElement, event); - showTooltip({ - tooltipLeft: coords!.x, - tooltipTop: coords!.y, - tooltipData: datum, - }); - }; - const showLabel = displayLabels.length > 0; - const total = values.reduce((acc, item) => item.numeric + acc, 0); + const total = fieldDisplayValues.reduce((acc, item) => item.display.numeric + acc, 0); const layout = getPieLayout(width, height, pieType); + const colors = [ + ...new Set(fieldDisplayValues.map((fieldDisplayValue) => fieldDisplayValue.display.color ?? FALLBACK_COLOR)), + ]; return (
- {values.map((value) => { - const color = value.color ?? FALLBACK_COLOR; + {colors.map((color) => { return ( = ({ ); })} = ({ > {(pie) => { return pie.arcs.map((arc) => { - return ( - onMouseMoveOverArc(event, arc.data)} - onMouseOut={hideTooltip} - > - - {showLabel && ( - - )} - - ); + const color = arc.data.display.color ?? FALLBACK_COLOR; + const label = showLabel ? ( + + ) : undefined; + if (arc.data.hasLinks && arc.data.getLinks) { + return ( + + {(api) => ( + + {label} + + )} + + ); + } else { + return ( + + {label} + + ); + } }); }} - {tooltipOpen && ( - - {tooltipData!.title} {formattedValueToString(tooltipData!)} + {tooltip.tooltipOpen && ( + + {tooltip.tooltipData!.title} {formattedValueToString(tooltip.tooltipData!)} )}
); }; +const PieSlice: FC<{ + children: ReactNode; + arc: PieArcDatum; + pie: ProvidedProps; + fill: string; + tooltip: UseTooltipParams; + openMenu?: (event: React.MouseEvent) => void; +}> = ({ arc, children, pie, openMenu, fill, tooltip }) => { + const theme = useTheme(); + const styles = useStyles(getStyles); + + const onMouseMoveOverArc = (event: any, datum: any) => { + const coords = localPoint(event.target.ownerSVGElement, event); + tooltip.showTooltip({ + tooltipLeft: coords!.x, + tooltipTop: coords!.y, + tooltipData: datum, + }); + }; + + return ( + onMouseMoveOverArc(event, arc.data.display)} + onMouseOut={tooltip.hideTooltip} + onClick={openMenu} + > + + {children} + + ); +}; + const PieLabel: FC<{ - arc: PieArcDatum; + arc: PieArcDatum; outerRadius: number; innerRadius: number; displayLabels: PieChartLabels[]; @@ -259,17 +306,17 @@ const PieLabel: FC<{ > {displayLabels.includes(PieChartLabels.Name) && ( - {arc.data.title} + {arc.data.display.title} )} {displayLabels.includes(PieChartLabels.Value) && ( - {formattedValueToString(arc.data)} + {formattedValueToString(arc.data.display)} )} {displayLabels.includes(PieChartLabels.Percent) && ( - {((arc.data.numeric / total) * 100).toFixed(0) + '%'} + {((arc.data.display.numeric / total) * 100).toFixed(0) + '%'} )} @@ -277,7 +324,7 @@ const PieLabel: FC<{ ); }; -function getLabelPos(arc: PieArcDatum, outerRadius: number, innerRadius: number) { +function getLabelPos(arc: PieArcDatum, outerRadius: number, innerRadius: number) { const r = (outerRadius + innerRadius) / 2; const a = (+arc.startAngle + +arc.endAngle) / 2 - Math.PI / 2; return [Math.cos(a) * r, Math.sin(a) * r]; @@ -337,5 +384,8 @@ const getStyles = (theme: GrafanaTheme) => { transform: scale3d(1.03, 1.03, 1); } `, + tooltipPortal: css` + z-index: 1050; + `, }; }; diff --git a/packages/grafana-ui/src/components/VizLegend/VizLegendList.tsx b/packages/grafana-ui/src/components/VizLegend/VizLegendList.tsx index 92d7988c8b7..3d52d71b3f1 100644 --- a/packages/grafana-ui/src/components/VizLegend/VizLegendList.tsx +++ b/packages/grafana-ui/src/components/VizLegend/VizLegendList.tsx @@ -29,7 +29,7 @@ export const VizLegendList: React.FunctionComponent = ({ ); } - const getItemKey = (item: VizLegendItem) => `${item.label}`; + const getItemKey = (item: VizLegendItem) => `${item.getItemKey ? item.getItemKey() : item.label}`; switch (placement) { case 'right': { diff --git a/packages/grafana-ui/src/components/VizLegend/types.ts b/packages/grafana-ui/src/components/VizLegend/types.ts index a078b0c8236..3a966ead64d 100644 --- a/packages/grafana-ui/src/components/VizLegend/types.ts +++ b/packages/grafana-ui/src/components/VizLegend/types.ts @@ -20,6 +20,7 @@ export interface LegendProps extends VizLegendBaseProps, VizLegendTableProps { } export interface VizLegendItem { + getItemKey?: () => string; label: string; color: string; yAxis: number; diff --git a/public/app/plugins/panel/piechart/PieChartPanel.tsx b/public/app/plugins/panel/piechart/PieChartPanel.tsx index cc341fb90ed..258e81d4d81 100644 --- a/public/app/plugins/panel/piechart/PieChartPanel.tsx +++ b/public/app/plugins/panel/piechart/PieChartPanel.tsx @@ -23,20 +23,20 @@ export const PieChartPanel: React.FC = ({ [fieldConfig, onFieldConfigChange] ); - const values = getFieldDisplayValues({ + const fieldDisplayValues = getFieldDisplayValues({ fieldConfig, reduceOptions: options.reduceOptions, data: data.series, theme: useTheme(), replaceVariables: replaceVariables, timeZone, - }).map((v) => v.display); + }); return (