mirror of
https://github.com/grafana/grafana.git
synced 2025-02-16 18:34:52 -06:00
BarChart: enable series toggling via legend (#33955)
* Adds support for Tooltip in BarChartPanel * Revert some formatting * Remove BarChart story * BarChart: move visualization to core * BarChart: enable series toggling via legend Co-authored-by: Ryan McKinley <ryantxu@gmail.com>
This commit is contained in:
parent
24b878ca4f
commit
2ded2aef71
@ -41,7 +41,7 @@ export interface GraphNGProps extends Themeable2 {
|
||||
prepConfig: (alignedFrame: DataFrame, getTimeRange: () => TimeRange) => UPlotConfigBuilder;
|
||||
propsToDiff?: string[];
|
||||
preparePlotFrame?: (frames: DataFrame[], dimFields: XYFieldMatchers) => DataFrame;
|
||||
renderLegend: (config: UPlotConfigBuilder) => React.ReactElement;
|
||||
renderLegend: (config: UPlotConfigBuilder) => React.ReactElement | null;
|
||||
}
|
||||
|
||||
function sameProps(prevProps: any, nextProps: any, propsToDiff: string[] = []) {
|
||||
|
@ -23,22 +23,13 @@ export class UnthemedTimeSeries extends React.Component<TimeSeriesProps> {
|
||||
};
|
||||
|
||||
renderLegend = (config: UPlotConfigBuilder) => {
|
||||
const { legend, onLegendClick, frames } = this.props;
|
||||
const { legend, frames } = this.props;
|
||||
|
||||
if (!config || (legend && legend.displayMode === LegendDisplayMode.Hidden)) {
|
||||
return;
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<PlotLegend
|
||||
data={frames}
|
||||
config={config}
|
||||
onLegendClick={onLegendClick}
|
||||
maxHeight="35%"
|
||||
maxWidth="60%"
|
||||
{...legend}
|
||||
/>
|
||||
);
|
||||
return <PlotLegend data={frames} config={config} maxHeight="35%" maxWidth="60%" {...legend} />;
|
||||
};
|
||||
|
||||
render() {
|
||||
|
@ -9,7 +9,7 @@ import { LegendPlacement } from '..';
|
||||
export interface VizLayoutProps {
|
||||
width: number;
|
||||
height: number;
|
||||
legend?: React.ReactElement<VizLayoutLegendProps>;
|
||||
legend?: React.ReactElement<VizLayoutLegendProps> | null;
|
||||
children: (width: number, height: number) => React.ReactNode;
|
||||
}
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
import React, { useCallback } from 'react';
|
||||
import React from 'react';
|
||||
import { DataFrame, DisplayValue, fieldReducers, getFieldDisplayName, reduceField } from '@grafana/data';
|
||||
import { UPlotConfigBuilder } from './config/UPlotConfigBuilder';
|
||||
import { VizLegendItem } from '../VizLegend/types';
|
||||
@ -6,42 +6,22 @@ import { VizLegendOptions } from '../VizLegend/models.gen';
|
||||
import { AxisPlacement } from './config';
|
||||
import { VizLayout, VizLayoutLegendProps } from '../VizLayout/VizLayout';
|
||||
import { VizLegend } from '../VizLegend/VizLegend';
|
||||
import { GraphNGLegendEvent } from '..';
|
||||
import { mapMouseEventToMode } from '../VizLegend/utils';
|
||||
|
||||
const defaultFormatter = (v: any) => (v == null ? '-' : v.toFixed(1));
|
||||
|
||||
interface PlotLegendProps extends VizLegendOptions, Omit<VizLayoutLegendProps, 'children'> {
|
||||
data: DataFrame[];
|
||||
config: UPlotConfigBuilder;
|
||||
onLegendClick?: (event: GraphNGLegendEvent) => void;
|
||||
}
|
||||
|
||||
export const PlotLegend: React.FC<PlotLegendProps> = ({
|
||||
data,
|
||||
config,
|
||||
onLegendClick,
|
||||
placement,
|
||||
calcs,
|
||||
displayMode,
|
||||
...vizLayoutLegendProps
|
||||
}) => {
|
||||
const onLegendLabelClick = useCallback(
|
||||
(legend: VizLegendItem, event: React.MouseEvent) => {
|
||||
const { fieldIndex } = legend;
|
||||
|
||||
if (!onLegendClick || !fieldIndex) {
|
||||
return;
|
||||
}
|
||||
|
||||
onLegendClick({
|
||||
fieldIndex,
|
||||
mode: mapMouseEventToMode(event),
|
||||
});
|
||||
},
|
||||
[onLegendClick]
|
||||
);
|
||||
|
||||
const legendItems = config
|
||||
.getSeries()
|
||||
.map<VizLegendItem | undefined>((s) => {
|
||||
@ -92,12 +72,7 @@ export const PlotLegend: React.FC<PlotLegendProps> = ({
|
||||
|
||||
return (
|
||||
<VizLayout.Legend placement={placement} {...vizLayoutLegendProps}>
|
||||
<VizLegend
|
||||
onLabelClick={onLegendLabelClick}
|
||||
placement={placement}
|
||||
items={legendItems}
|
||||
displayMode={displayMode}
|
||||
/>
|
||||
<VizLegend placement={placement} items={legendItems} displayMode={displayMode} />
|
||||
</VizLayout.Legend>
|
||||
);
|
||||
};
|
||||
|
@ -1,6 +1,14 @@
|
||||
import React from 'react';
|
||||
import { DataFrame, TimeRange } from '@grafana/data';
|
||||
import { GraphNG, GraphNGProps, LegendDisplayMode, PlotLegend, UPlotConfigBuilder, withTheme2 } from '@grafana/ui';
|
||||
import {
|
||||
GraphNG,
|
||||
GraphNGProps,
|
||||
LegendDisplayMode,
|
||||
PlotLegend,
|
||||
UPlotConfigBuilder,
|
||||
usePanelContext,
|
||||
useTheme2,
|
||||
} from '@grafana/ui';
|
||||
import { BarChartOptions } from './types';
|
||||
import { preparePlotConfigBuilder, preparePlotFrame } from './utils';
|
||||
|
||||
@ -9,14 +17,24 @@ import { preparePlotConfigBuilder, preparePlotFrame } from './utils';
|
||||
*/
|
||||
export interface BarChartProps
|
||||
extends BarChartOptions,
|
||||
Omit<GraphNGProps, 'prepConfig' | 'propsToDiff' | 'renderLegend'> {}
|
||||
Omit<GraphNGProps, 'prepConfig' | 'propsToDiff' | 'renderLegend' | 'theme'> {}
|
||||
|
||||
const propsToDiff: string[] = ['orientation', 'barWidth', 'groupWidth', 'showValue'];
|
||||
|
||||
class UnthemedBarChart extends React.Component<BarChartProps> {
|
||||
prepConfig = (alignedFrame: DataFrame, getTimeRange: () => TimeRange) => {
|
||||
const { eventBus } = this.context;
|
||||
const { theme, timeZone, orientation, barWidth, showValue, groupWidth, stacking, legend, tooltip } = this.props;
|
||||
export const BarChart: React.FC<BarChartProps> = (props) => {
|
||||
const theme = useTheme2();
|
||||
const { eventBus } = usePanelContext();
|
||||
|
||||
const renderLegend = (config: UPlotConfigBuilder) => {
|
||||
if (!config || props.legend.displayMode === LegendDisplayMode.Hidden) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return <PlotLegend data={props.frames} config={config} maxHeight="35%" maxWidth="60%" {...props.legend} />;
|
||||
};
|
||||
|
||||
const prepConfig = (alignedFrame: DataFrame, getTimeRange: () => TimeRange) => {
|
||||
const { timeZone, orientation, barWidth, showValue, groupWidth, stacking, legend, tooltip } = props;
|
||||
return preparePlotConfigBuilder({
|
||||
frame: alignedFrame,
|
||||
getTimeRange,
|
||||
@ -33,38 +51,16 @@ class UnthemedBarChart extends React.Component<BarChartProps> {
|
||||
});
|
||||
};
|
||||
|
||||
renderLegend = (config: UPlotConfigBuilder) => {
|
||||
const { legend, onLegendClick, frames } = this.props;
|
||||
|
||||
if (!config || legend.displayMode === LegendDisplayMode.Hidden) {
|
||||
return;
|
||||
}
|
||||
|
||||
return (
|
||||
<PlotLegend
|
||||
data={frames}
|
||||
config={config}
|
||||
onLegendClick={onLegendClick}
|
||||
maxHeight="35%"
|
||||
maxWidth="60%"
|
||||
{...legend}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
<GraphNG
|
||||
{...this.props}
|
||||
frames={this.props.frames}
|
||||
prepConfig={this.prepConfig}
|
||||
propsToDiff={propsToDiff}
|
||||
preparePlotFrame={preparePlotFrame}
|
||||
renderLegend={this.renderLegend as any}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export const BarChart = withTheme2(UnthemedBarChart);
|
||||
return (
|
||||
<GraphNG
|
||||
{...props}
|
||||
theme={theme}
|
||||
frames={props.frames}
|
||||
prepConfig={prepConfig}
|
||||
propsToDiff={propsToDiff}
|
||||
preparePlotFrame={preparePlotFrame}
|
||||
renderLegend={renderLegend}
|
||||
/>
|
||||
);
|
||||
};
|
||||
BarChart.displayName = 'BarChart';
|
||||
|
@ -1,7 +1,6 @@
|
||||
import React, { useCallback, useMemo } from 'react';
|
||||
import React, { useMemo } from 'react';
|
||||
import { FieldType, PanelProps, TimeRange, VizOrientation } from '@grafana/data';
|
||||
import { GraphNGLegendEvent, TooltipPlugin } from '@grafana/ui';
|
||||
import { hideSeriesConfigFactory } from '../timeseries/overrides/hideSeriesConfigFactory';
|
||||
import { TooltipPlugin } from '@grafana/ui';
|
||||
import { BarChartOptions } from './types';
|
||||
import { BarChart } from './BarChart';
|
||||
|
||||
@ -10,15 +9,7 @@ interface Props extends PanelProps<BarChartOptions> {}
|
||||
/**
|
||||
* @alpha
|
||||
*/
|
||||
export const BarChartPanel: React.FunctionComponent<Props> = ({
|
||||
data,
|
||||
options,
|
||||
width,
|
||||
height,
|
||||
fieldConfig,
|
||||
timeZone,
|
||||
onFieldConfigChange,
|
||||
}) => {
|
||||
export const BarChartPanel: React.FunctionComponent<Props> = ({ data, options, width, height, timeZone }) => {
|
||||
const orientation = useMemo(() => {
|
||||
if (!options.orientation || options.orientation === VizOrientation.Auto) {
|
||||
return width < height ? VizOrientation.Horizontal : VizOrientation.Vertical;
|
||||
@ -27,13 +18,6 @@ export const BarChartPanel: React.FunctionComponent<Props> = ({
|
||||
return options.orientation;
|
||||
}, [width, height, options.orientation]);
|
||||
|
||||
const onLegendClick = useCallback(
|
||||
(event: GraphNGLegendEvent) => {
|
||||
onFieldConfigChange(hideSeriesConfigFactory(event, fieldConfig, data.series));
|
||||
},
|
||||
[fieldConfig, onFieldConfigChange, data.series]
|
||||
);
|
||||
|
||||
if (!data || !data.series?.length) {
|
||||
return (
|
||||
<div className="panel-empty">
|
||||
@ -66,7 +50,6 @@ export const BarChartPanel: React.FunctionComponent<Props> = ({
|
||||
structureRev={data.structureRev}
|
||||
width={width}
|
||||
height={height}
|
||||
onLegendClick={onLegendClick}
|
||||
{...options}
|
||||
orientation={orientation}
|
||||
>
|
||||
|
@ -18,8 +18,8 @@ import {
|
||||
ScaleDistribution,
|
||||
ScaleOrientation,
|
||||
UPlotConfigBuilder,
|
||||
UPlotConfigPrepFn,
|
||||
} from '@grafana/ui';
|
||||
import { UPlotConfigPrepFn } from '@grafana/ui/src/components/uPlot/config/UPlotConfigBuilder';
|
||||
|
||||
/** @alpha */
|
||||
function getBarCharScaleOrientation(orientation: VizOrientation) {
|
||||
|
@ -1,12 +1,5 @@
|
||||
import React from 'react';
|
||||
import {
|
||||
PanelContext,
|
||||
PanelContextRoot,
|
||||
UPlotConfigBuilder,
|
||||
GraphNG,
|
||||
GraphNGProps,
|
||||
BarValueVisibility,
|
||||
} from '@grafana/ui';
|
||||
import { PanelContext, PanelContextRoot, GraphNG, GraphNGProps, BarValueVisibility } from '@grafana/ui';
|
||||
import { DataFrame, FieldType, TimeRange } from '@grafana/data';
|
||||
import { preparePlotConfigBuilder } from './utils';
|
||||
import { TimelineMode } from './types';
|
||||
@ -39,9 +32,7 @@ export class TimelineChart extends React.Component<TimelineProps> {
|
||||
});
|
||||
};
|
||||
|
||||
renderLegend = (config: UPlotConfigBuilder) => {
|
||||
return;
|
||||
};
|
||||
renderLegend = () => null;
|
||||
|
||||
render() {
|
||||
return (
|
||||
@ -53,7 +44,7 @@ export class TimelineChart extends React.Component<TimelineProps> {
|
||||
}}
|
||||
prepConfig={this.prepConfig}
|
||||
propsToDiff={propsToDiff}
|
||||
renderLegend={this.renderLegend as any}
|
||||
renderLegend={this.renderLegend}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
@ -1,7 +1,6 @@
|
||||
import React, { useCallback } from 'react';
|
||||
import React from 'react';
|
||||
import { PanelProps } from '@grafana/data';
|
||||
import { GraphNGLegendEvent, useTheme2 } from '@grafana/ui';
|
||||
import { hideSeriesConfigFactory } from '../timeseries/overrides/hideSeriesConfigFactory';
|
||||
import { useTheme2 } from '@grafana/ui';
|
||||
import { TimelineOptions } from './types';
|
||||
import { TimelineChart } from './TimelineChart';
|
||||
|
||||
@ -10,25 +9,9 @@ interface TimelinePanelProps extends PanelProps<TimelineOptions> {}
|
||||
/**
|
||||
* @alpha
|
||||
*/
|
||||
export const TimelinePanel: React.FC<TimelinePanelProps> = ({
|
||||
data,
|
||||
timeRange,
|
||||
timeZone,
|
||||
options,
|
||||
width,
|
||||
height,
|
||||
fieldConfig,
|
||||
onFieldConfigChange,
|
||||
}) => {
|
||||
export const TimelinePanel: React.FC<TimelinePanelProps> = ({ data, timeRange, timeZone, options, width, height }) => {
|
||||
const theme = useTheme2();
|
||||
|
||||
const onLegendClick = useCallback(
|
||||
(event: GraphNGLegendEvent) => {
|
||||
onFieldConfigChange(hideSeriesConfigFactory(event, fieldConfig, data.series));
|
||||
},
|
||||
[fieldConfig, onFieldConfigChange, data.series]
|
||||
);
|
||||
|
||||
if (!data || !data.series?.length) {
|
||||
return (
|
||||
<div className="panel-empty">
|
||||
@ -46,7 +29,6 @@ export const TimelinePanel: React.FC<TimelinePanelProps> = ({
|
||||
timeZone={timeZone}
|
||||
width={width}
|
||||
height={height}
|
||||
onLegendClick={onLegendClick}
|
||||
{...options}
|
||||
/>
|
||||
);
|
||||
|
Loading…
Reference in New Issue
Block a user