VizLegend: move onSeriesColorChanged to PanelContext (breaking change) (#33611)

This commit is contained in:
Ryan McKinley 2021-05-03 08:23:17 -07:00 committed by GitHub
parent 578283078b
commit 6b0417207d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
22 changed files with 51 additions and 149 deletions

View File

@ -22,7 +22,6 @@ export interface BarChartProps extends Themeable2, BarChartOptions {
data: DataFrame[];
structureRev?: number; // a number that will change when the data[] structure changes
onLegendClick?: (event: GraphNGLegendEvent) => void;
onSeriesColorChange?: (label: string, color: string) => void;
}
interface BarChartState {
@ -87,7 +86,7 @@ class UnthemedBarChart extends React.Component<BarChartProps, BarChartState> {
}
renderLegend() {
const { legend, onSeriesColorChange, onLegendClick, data } = this.props;
const { legend, onLegendClick, data } = this.props;
const { config } = this.state;
if (!config || legend.displayMode === LegendDisplayMode.Hidden) {
@ -97,7 +96,6 @@ class UnthemedBarChart extends React.Component<BarChartProps, BarChartState> {
<PlotLegend
data={data}
config={config}
onSeriesColorChange={onSeriesColorChange}
onLegendClick={onLegendClick}
maxHeight="35%"
maxWidth="60%"

View File

@ -5,7 +5,7 @@ import { css } from '@emotion/css';
import { GraphSeriesValue } from '@grafana/data';
import { Graph, GraphProps } from './Graph';
import { VizLegendItem, SeriesColorChangeHandler } from '../VizLegend/types';
import { VizLegendItem } from '../VizLegend/types';
import { LegendDisplayMode, LegendPlacement } from '../VizLegend/models.gen';
import { VizLegend } from '../VizLegend/VizLegend';
import { CustomScrollbar } from '../CustomScrollbar/CustomScrollbar';
@ -18,7 +18,6 @@ export interface GraphWithLegendProps extends GraphProps {
hideZero?: boolean;
sortLegendBy?: string;
sortLegendDesc?: boolean;
onSeriesColorChange?: SeriesColorChangeHandler;
onSeriesToggle?: (label: string, event: React.MouseEvent<HTMLElement>) => void;
onToggleSort: (sortBy: string) => void;
}
@ -58,7 +57,6 @@ export const GraphWithLegend: React.FunctionComponent<GraphWithLegendProps> = (p
sortLegendDesc,
legendDisplayMode,
placement,
onSeriesColorChange,
onSeriesToggle,
onToggleSort,
hideEmpty,
@ -120,7 +118,6 @@ export const GraphWithLegend: React.FunctionComponent<GraphWithLegendProps> = (p
onSeriesToggle(item.label, event);
}
}}
onSeriesColorChange={onSeriesColorChange}
onToggleSort={onToggleSort}
/>
</CustomScrollbar>

View File

@ -27,7 +27,6 @@ export interface GraphNGProps extends Themeable2 {
timeZone: TimeZone;
fields?: XYFieldMatchers; // default will assume timeseries data
onLegendClick?: (event: GraphNGLegendEvent) => void;
onSeriesColorChange?: (label: string, color: string) => void;
children?: (builder: UPlotConfigBuilder, alignedDataFrame: DataFrame) => React.ReactNode;
}
@ -114,7 +113,7 @@ class UnthemedGraphNG extends React.Component<GraphNGProps, GraphNGState> {
};
renderLegend() {
const { legend, onSeriesColorChange, onLegendClick, data } = this.props;
const { legend, onLegendClick, data } = this.props;
const { config } = this.state;
if (!config || (legend && legend.displayMode === LegendDisplayMode.Hidden)) {
@ -125,7 +124,6 @@ class UnthemedGraphNG extends React.Component<GraphNGProps, GraphNGState> {
<PlotLegend
data={data}
config={config}
onSeriesColorChange={onSeriesColorChange}
onLegendClick={onLegendClick}
maxHeight="35%"
maxWidth="60%"

View File

@ -4,6 +4,13 @@ import React from 'react';
/** @alpha */
export interface PanelContext {
eventBus: EventBus;
/**
* Called when a component wants to change the color for a series
*
* @alpha -- experimental
*/
onSeriesColorChange?: (label: string, color: string) => void;
}
const PanelContextRoot = React.createContext<PanelContext>({

View File

@ -53,7 +53,6 @@ export function PieChart(props: PieChartProps) {
fieldConfig,
replaceVariables,
tooltipOptions,
onSeriesColorChange,
width,
height,
...restProps
@ -128,14 +127,7 @@ function getLegend(props: PieChartProps, displayValues: FieldDisplay[]) {
};
});
return (
<VizLegend
items={legendItems}
onSeriesColorChange={props.onSeriesColorChange}
placement={legendOptions.placement}
displayMode={legendOptions.displayMode}
/>
);
return <VizLegend items={legendItems} placement={legendOptions.placement} displayMode={legendOptions.displayMode} />;
}
function useSliceHighlightState() {

View File

@ -13,7 +13,6 @@ export interface PieChartSvgProps {
highlightedTitle?: string;
displayLabels?: PieChartLabels[];
useGradients?: boolean;
onSeriesColorChange?: (label: string, color: string) => void;
tooltipOptions: VizTooltipOptions;
}
@ -26,7 +25,6 @@ export interface PieChartProps {
pieType: PieChartType;
displayLabels?: PieChartLabels[];
useGradients?: boolean;
onSeriesColorChange?: (label: string, color: string) => void;
legendOptions?: PieChartLegendOptions;
tooltipOptions: VizTooltipOptions;
reduceOptions: ReduceDataOptions;

View File

@ -100,7 +100,7 @@ class UnthemedTimelineChart extends React.Component<TimelineProps, GraphNGState>
};
renderLegend() {
const { legend, onSeriesColorChange, onLegendClick, data } = this.props;
const { legend, onLegendClick, data } = this.props;
const { config } = this.state;
if (!config || (legend && legend.displayMode === LegendDisplayMode.Hidden)) {
@ -111,7 +111,6 @@ class UnthemedTimelineChart extends React.Component<TimelineProps, GraphNGState>
<PlotLegend
data={data}
config={config}
onSeriesColorChange={onSeriesColorChange}
onLegendClick={onLegendClick}
maxHeight="35%"
maxWidth="60%"

View File

@ -53,21 +53,6 @@ const LegendStoryDemo: FC<LegendStoryDemoProps> = ({ displayMode, seriesCount, n
setItems(generateLegendItems(seriesCount, theme, stats));
}, [seriesCount, theme, stats]);
const onSeriesColorChange = (label: string, color: string) => {
setItems(
items.map((item) => {
if (item.label === label) {
return {
...item,
color: color,
};
}
return item;
})
);
};
const onLabelClick = (clickItem: VizLegendItem) => {
setItems(
items.map((item) => {
@ -89,13 +74,7 @@ const LegendStoryDemo: FC<LegendStoryDemoProps> = ({ displayMode, seriesCount, n
return (
<p style={{ marginBottom: '32px' }}>
<h3 style={{ marginBottom: '32px' }}>{name}</h3>
<VizLegend
displayMode={displayMode}
items={items}
placement={placement}
onSeriesColorChange={onSeriesColorChange}
onLabelClick={onLabelClick}
/>
<VizLegend displayMode={displayMode} items={items} placement={placement} onLabelClick={onLabelClick} />
</p>
);
};

View File

@ -16,7 +16,6 @@ export const VizLegend: React.FunctionComponent<LegendProps> = ({
sortDesc,
onToggleSort,
onLabelClick,
onSeriesColorChange,
placement,
className,
}) => {
@ -65,7 +64,6 @@ export const VizLegend: React.FunctionComponent<LegendProps> = ({
onToggleSort={onToggleSort}
onLabelMouseEnter={onMouseEnter}
onLabelMouseOut={onMouseOut}
onSeriesColorChange={onSeriesColorChange}
/>
);
case LegendDisplayMode.List:
@ -77,7 +75,6 @@ export const VizLegend: React.FunctionComponent<LegendProps> = ({
onLabelMouseEnter={onMouseEnter}
onLabelMouseOut={onMouseOut}
onLabelClick={onLabelClick}
onSeriesColorChange={onSeriesColorChange}
/>
);
default:

View File

@ -15,7 +15,6 @@ export interface Props extends VizLegendBaseProps {}
export const VizLegendList: React.FunctionComponent<Props> = ({
items,
itemRenderer,
onSeriesColorChange,
onLabelClick,
onLabelMouseEnter,
onLabelMouseOut,
@ -30,7 +29,6 @@ export const VizLegendList: React.FunctionComponent<Props> = ({
<VizLegendListItem
item={item}
onLabelClick={onLabelClick}
onSeriesColorChange={onSeriesColorChange}
onLabelMouseEnter={onLabelMouseEnter}
onLabelMouseOut={onLabelMouseOut}
/>

View File

@ -1,7 +1,7 @@
import React, { useCallback } from 'react';
import { css, cx } from '@emotion/css';
import { VizLegendSeriesIcon } from './VizLegendSeriesIcon';
import { VizLegendItem, SeriesColorChangeHandler } from './types';
import { VizLegendItem } from './types';
import { VizLegendStatsList } from './VizLegendStatsList';
import { useStyles } from '../../themes';
import { GrafanaTheme } from '@grafana/data';
@ -10,7 +10,6 @@ export interface Props {
item: VizLegendItem;
className?: string;
onLabelClick?: (item: VizLegendItem, event: React.MouseEvent<HTMLDivElement>) => void;
onSeriesColorChange?: SeriesColorChangeHandler;
onLabelMouseEnter?: (item: VizLegendItem, event: React.MouseEvent<HTMLDivElement>) => void;
onLabelMouseOut?: (item: VizLegendItem, event: React.MouseEvent<HTMLDivElement>) => void;
}
@ -20,7 +19,6 @@ export interface Props {
*/
export const VizLegendListItem: React.FunctionComponent<Props> = ({
item,
onSeriesColorChange,
onLabelClick,
onLabelMouseEnter,
onLabelMouseOut,
@ -54,18 +52,9 @@ export const VizLegendListItem: React.FunctionComponent<Props> = ({
[item, onLabelClick]
);
const onColorChange = useCallback(
(color: string) => {
if (onSeriesColorChange) {
onSeriesColorChange(item.label, color);
}
},
[item, onSeriesColorChange]
);
return (
<div className={styles.itemWrapper}>
<VizLegendSeriesIcon disabled={!onSeriesColorChange} color={item.color} onColorChange={onColorChange} />
<VizLegendSeriesIcon seriesName={item.label} color={item.color} />
<div
onMouseEnter={onMouseEnter}
onMouseOut={onMouseOut}

View File

@ -1,32 +1,41 @@
import React from 'react';
import React, { useCallback } from 'react';
import { SeriesColorPicker } from '../ColorPicker/ColorPicker';
import { usePanelContext } from '../PanelChrome';
import { SeriesIcon } from './SeriesIcon';
interface Props {
disabled: boolean;
seriesName: string;
color: string;
onColorChange: (color: string) => void;
}
/**
* @internal
*/
export const VizLegendSeriesIcon: React.FunctionComponent<Props> = ({ disabled, color, onColorChange }) => {
return disabled ? (
<SeriesIcon color={color} />
) : (
<SeriesColorPicker color={color} onChange={onColorChange} enableNamedColors>
{({ ref, showColorPicker, hideColorPicker }) => (
<SeriesIcon
color={color}
className="pointer"
ref={ref}
onClick={showColorPicker}
onMouseLeave={hideColorPicker}
/>
)}
</SeriesColorPicker>
export const VizLegendSeriesIcon: React.FunctionComponent<Props> = ({ seriesName, color }) => {
const { onSeriesColorChange } = usePanelContext();
const onChange = useCallback(
(color: string) => {
return onSeriesColorChange!(seriesName, color);
},
[seriesName, onSeriesColorChange]
);
if (seriesName && onSeriesColorChange) {
return (
<SeriesColorPicker color={color} onChange={onChange} enableNamedColors>
{({ ref, showColorPicker, hideColorPicker }) => (
<SeriesIcon
color={color}
className="pointer"
ref={ref}
onClick={showColorPicker}
onMouseLeave={hideColorPicker}
/>
)}
</SeriesColorPicker>
);
}
return <SeriesIcon color={color} />;
};
VizLegendSeriesIcon.displayName = 'VizLegendSeriesIcon';

View File

@ -20,7 +20,6 @@ export const VizLegendTable: FC<VizLegendTableProps> = ({
onLabelClick,
onLabelMouseEnter,
onLabelMouseOut,
onSeriesColorChange,
}) => {
const styles = useStyles(getStyles);
@ -57,7 +56,6 @@ export const VizLegendTable: FC<VizLegendTableProps> = ({
<LegendTableItem
key={`${item.label}-${index}`}
item={item}
onSeriesColorChange={onSeriesColorChange}
onLabelClick={onLabelClick}
onLabelMouseEnter={onLabelMouseEnter}
onLabelMouseOut={onLabelMouseOut}

View File

@ -1,7 +1,7 @@
import React, { useCallback } from 'react';
import { css, cx } from '@emotion/css';
import { VizLegendSeriesIcon } from './VizLegendSeriesIcon';
import { VizLegendItem, SeriesColorChangeHandler } from './types';
import { VizLegendItem } from './types';
import { useStyles } from '../../themes/ThemeContext';
import { styleMixins } from '../../themes';
import { GrafanaTheme, formattedValueToString } from '@grafana/data';
@ -11,7 +11,6 @@ export interface Props {
item: VizLegendItem;
className?: string;
onLabelClick?: (item: VizLegendItem, event: React.MouseEvent<HTMLDivElement>) => void;
onSeriesColorChange?: SeriesColorChangeHandler;
onLabelMouseEnter?: (item: VizLegendItem, event: React.MouseEvent<HTMLDivElement>) => void;
onLabelMouseOut?: (item: VizLegendItem, event: React.MouseEvent<HTMLDivElement>) => void;
}
@ -21,7 +20,6 @@ export interface Props {
*/
export const LegendTableItem: React.FunctionComponent<Props> = ({
item,
onSeriesColorChange,
onLabelClick,
onLabelMouseEnter,
onLabelMouseOut,
@ -56,20 +54,11 @@ export const LegendTableItem: React.FunctionComponent<Props> = ({
[item, onLabelClick]
);
const onColorChange = useCallback(
(color: string) => {
if (onSeriesColorChange) {
onSeriesColorChange(item.label, color);
}
},
[item, onSeriesColorChange]
);
return (
<tr className={cx(styles.row, className)}>
<td>
<span className={styles.itemWrapper}>
<VizLegendSeriesIcon disabled={!onSeriesColorChange} color={item.color} onColorChange={onColorChange} />
<VizLegendSeriesIcon color={item.color} seriesName={item.label} />
<div
onMouseEnter={onMouseEnter}
onMouseOut={onMouseOut}

View File

@ -7,7 +7,6 @@ export interface VizLegendBaseProps {
className?: string;
items: VizLegendItem[];
itemRenderer?: (item: VizLegendItem, index: number) => JSX.Element;
onSeriesColorChange?: SeriesColorChangeHandler;
onLabelClick?: (item: VizLegendItem, event: React.MouseEvent<HTMLElement>) => void;
onLabelMouseEnter?: (item: VizLegendItem, event: React.MouseEvent<HTMLElement>) => void;
onLabelMouseOut?: (item: VizLegendItem, event: React.MouseEvent<HTMLElement>) => void;
@ -33,6 +32,3 @@ export interface VizLegendItem {
getDisplayValues?: () => DisplayValue[];
fieldIndex?: DataFrameFieldIndex;
}
export type SeriesOptionChangeHandler<TOption> = (label: string, option: TOption) => void;
export type SeriesColorChangeHandler = SeriesOptionChangeHandler<string>;

View File

@ -14,14 +14,12 @@ const defaultFormatter = (v: any) => (v == null ? '-' : v.toFixed(1));
interface PlotLegendProps extends VizLegendOptions, Omit<VizLayoutLegendProps, 'children'> {
data: DataFrame[];
config: UPlotConfigBuilder;
onSeriesColorChange?: (label: string, color: string) => void;
onLegendClick?: (event: GraphNGLegendEvent) => void;
}
export const PlotLegend: React.FC<PlotLegendProps> = ({
data,
config,
onSeriesColorChange,
onLegendClick,
placement,
calcs,
@ -99,7 +97,6 @@ export const PlotLegend: React.FC<PlotLegendProps> = ({
placement={placement}
items={legendItems}
displayMode={displayMode}
onSeriesColorChange={onSeriesColorChange}
/>
</VizLayout.Legend>
);

View File

@ -29,6 +29,7 @@ import {
import { selectors } from '@grafana/e2e-selectors';
import { loadSnapshotData } from '../utils/loadSnapshotData';
import { RefreshEvent, RenderEvent } from 'app/types/events';
import { changeSeriesColorConfigFactory } from 'app/plugins/panel/timeseries/overrides/colorSeriesConfigFactory';
const DEFAULT_PLUGIN_ERROR = 'Error in plugin';
@ -75,11 +76,16 @@ export class PanelChrome extends Component<Props, State> {
refreshWhenInView: false,
context: {
eventBus,
onSeriesColorChange: this.onSeriesColorChange,
},
data: this.getInitialPanelDataState(),
};
}
onSeriesColorChange = (label: string, color: string) => {
this.onFieldConfigChange(changeSeriesColorConfigFactory(label, color, this.props.panel.fieldConfig));
};
getInitialPanelDataState(): PanelData {
return {
state: LoadingState.NotStarted,

View File

@ -1,7 +1,6 @@
import React, { useCallback, useMemo } from 'react';
import { FieldType, PanelProps, VizOrientation } from '@grafana/data';
import { BarChart, BarChartOptions, GraphNGLegendEvent } from '@grafana/ui';
import { changeSeriesColorConfigFactory } from '../timeseries/overrides/colorSeriesConfigFactory';
import { hideSeriesConfigFactory } from '../timeseries/overrides/hideSeriesConfigFactory';
interface Props extends PanelProps<BarChartOptions> {}
@ -32,13 +31,6 @@ export const BarChartPanel: React.FunctionComponent<Props> = ({
[fieldConfig, onFieldConfigChange, data.series]
);
const onSeriesColorChange = useCallback(
(label: string, color: string) => {
onFieldConfigChange(changeSeriesColorConfigFactory(label, color, fieldConfig));
},
[fieldConfig, onFieldConfigChange]
);
if (!data || !data.series?.length) {
return (
<div className="panel-empty">
@ -70,7 +62,6 @@ export const BarChartPanel: React.FunctionComponent<Props> = ({
width={width}
height={height}
onLegendClick={onLegendClick}
onSeriesColorChange={onSeriesColorChange}
{...options}
orientation={orientation}
/>

View File

@ -1,8 +1,7 @@
import React, { useCallback } from 'react';
import React from 'react';
import { PieChart } from '@grafana/ui';
import { PieChartOptions } from './types';
import { PanelProps } from '@grafana/data';
import { changeSeriesColorConfigFactory } from '../timeseries/overrides/colorSeriesConfigFactory';
interface Props extends PanelProps<PieChartOptions> {}
@ -16,13 +15,6 @@ export const PieChartPanel: React.FC<Props> = ({
fieldConfig,
timeZone,
}) => {
const onSeriesColorChange = useCallback(
(label: string, color: string) => {
onFieldConfigChange(changeSeriesColorConfigFactory(label, color, fieldConfig));
},
[fieldConfig, onFieldConfigChange]
);
return (
<PieChart
width={width}
@ -32,7 +24,6 @@ export const PieChartPanel: React.FC<Props> = ({
reduceOptions={options.reduceOptions}
replaceVariables={replaceVariables}
data={data.series}
onSeriesColorChange={onSeriesColorChange}
pieType={options.pieType}
displayLabels={options.displayLabels}
legendOptions={options.legend}

View File

@ -1,7 +1,6 @@
import React, { useCallback } from 'react';
import { PanelProps } from '@grafana/data';
import { GraphNGLegendEvent, TimelineChart, TimelineOptions } from '@grafana/ui';
import { changeSeriesColorConfigFactory } from '../timeseries/overrides/colorSeriesConfigFactory';
import { hideSeriesConfigFactory } from '../timeseries/overrides/hideSeriesConfigFactory';
interface TimelinePanelProps extends PanelProps<TimelineOptions> {}
@ -26,13 +25,6 @@ export const TimelinePanel: React.FC<TimelinePanelProps> = ({
[fieldConfig, onFieldConfigChange, data.series]
);
const onSeriesColorChange = useCallback(
(label: string, color: string) => {
onFieldConfigChange(changeSeriesColorConfigFactory(label, color, fieldConfig));
},
[fieldConfig, onFieldConfigChange]
);
if (!data || !data.series?.length) {
return (
<div className="panel-empty">
@ -50,7 +42,6 @@ export const TimelinePanel: React.FC<TimelinePanelProps> = ({
width={width}
height={height}
onLegendClick={onLegendClick}
onSeriesColorChange={onSeriesColorChange}
{...options}
/>
);

View File

@ -2,7 +2,6 @@ import { Field, PanelProps } from '@grafana/data';
import { GraphNG, GraphNGLegendEvent, TooltipPlugin, ZoomPlugin } from '@grafana/ui';
import { getFieldLinksForExplore } from 'app/features/explore/utils/links';
import React, { useCallback } from 'react';
import { changeSeriesColorConfigFactory } from './overrides/colorSeriesConfigFactory';
import { hideSeriesConfigFactory } from './overrides/hideSeriesConfigFactory';
import { AnnotationsPlugin } from './plugins/AnnotationsPlugin';
import { ContextMenuPlugin } from './plugins/ContextMenuPlugin';
@ -34,13 +33,6 @@ export const TimeSeriesPanel: React.FC<TimeSeriesPanelProps> = ({
return getFieldLinksForExplore({ field, rowIndex, range: timeRange });
};
const onSeriesColorChange = useCallback(
(label: string, color: string) => {
onFieldConfigChange(changeSeriesColorConfigFactory(label, color, fieldConfig));
},
[fieldConfig, onFieldConfigChange]
);
if (!data || !data.series?.length) {
return (
<div className="panel-empty">
@ -59,7 +51,6 @@ export const TimeSeriesPanel: React.FC<TimeSeriesPanelProps> = ({
height={height}
legend={options.legend}
onLegendClick={onLegendClick}
onSeriesColorChange={onSeriesColorChange}
>
{(config, alignedDataFrame) => {
return (

View File

@ -4,7 +4,6 @@ import { PanelProps } from '@grafana/data';
import { Options } from './types';
import { hideSeriesConfigFactory } from '../timeseries/overrides/hideSeriesConfigFactory';
import { getXYDimensions } from './dims';
import { changeSeriesColorConfigFactory } from '../timeseries/overrides/colorSeriesConfigFactory';
interface XYChartPanelProps extends PanelProps<Options> {}
@ -29,13 +28,6 @@ export const XYChartPanel: React.FC<XYChartPanelProps> = ({
[fieldConfig, onFieldConfigChange, frames]
);
const onSeriesColorChange = useCallback(
(label: string, color: string) => {
onFieldConfigChange(changeSeriesColorConfigFactory(label, color, fieldConfig));
},
[fieldConfig, onFieldConfigChange]
);
if (dims.error) {
return (
<div>
@ -61,7 +53,6 @@ export const XYChartPanel: React.FC<XYChartPanelProps> = ({
height={height}
legend={options.legend}
onLegendClick={onLegendClick}
onSeriesColorChange={onSeriesColorChange}
>
{(config, alignedDataFrame) => {
return (