mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
GraphNG: added support to change series color from legend. (#30256)
* added support for changing color of series. * removed dependency on internal type.
This commit is contained in:
parent
e089365abe
commit
382c75d0db
@ -33,6 +33,7 @@ export interface GraphNGProps extends Omit<PlotProps, 'data' | 'config'> {
|
||||
legend?: VizLegendOptions;
|
||||
fields?: XYFieldMatchers; // default will assume timeseries data
|
||||
onLegendClick?: (event: GraphNGLegendEvent) => void;
|
||||
onSeriesColorChange?: (label: string, color: string) => void;
|
||||
}
|
||||
|
||||
const defaultConfig: GraphFieldConfig = {
|
||||
@ -51,6 +52,7 @@ export const GraphNG: React.FC<GraphNGProps> = ({
|
||||
timeRange,
|
||||
timeZone,
|
||||
onLegendClick,
|
||||
onSeriesColorChange,
|
||||
...plotProps
|
||||
}) => {
|
||||
const alignedFrameWithGapTest = useMemo(() => alignDataFrames(data, fields), [data, fields]);
|
||||
@ -222,6 +224,7 @@ export const GraphNG: React.FC<GraphNGProps> = ({
|
||||
placement={legend!.placement}
|
||||
items={legendItemsRef.current}
|
||||
displayMode={legend!.displayMode}
|
||||
onSeriesColorChange={onSeriesColorChange}
|
||||
/>
|
||||
</VizLayout.Legend>
|
||||
);
|
||||
|
@ -4,8 +4,9 @@ import { PanelProps } from '@grafana/data';
|
||||
import { Options } from './types';
|
||||
import { AnnotationsPlugin } from './plugins/AnnotationsPlugin';
|
||||
import { ExemplarsPlugin } from './plugins/ExemplarsPlugin';
|
||||
import { hideSeriesConfigFactory } from './hideSeriesConfigFactory';
|
||||
import { ContextMenuPlugin } from './plugins/ContextMenuPlugin';
|
||||
import { hideSeriesConfigFactory } from './overrides/hideSeriesConfigFactory';
|
||||
import { changeSeriesColorConfigFactory } from './overrides/colorSeriesConfigFactory';
|
||||
|
||||
interface TimeSeriesPanelProps extends PanelProps<Options> {}
|
||||
|
||||
@ -28,6 +29,13 @@ export const TimeSeriesPanel: React.FC<TimeSeriesPanelProps> = ({
|
||||
[fieldConfig, onFieldConfigChange, data.series]
|
||||
);
|
||||
|
||||
const onSeriesColorChange = useCallback(
|
||||
(label: string, color: string) => {
|
||||
onFieldConfigChange(changeSeriesColorConfigFactory(label, color, fieldConfig));
|
||||
},
|
||||
[fieldConfig, onFieldConfigChange]
|
||||
);
|
||||
|
||||
return (
|
||||
<GraphNG
|
||||
data={data.series}
|
||||
@ -37,6 +45,7 @@ export const TimeSeriesPanel: React.FC<TimeSeriesPanelProps> = ({
|
||||
height={height}
|
||||
legend={options.legend}
|
||||
onLegendClick={onLegendClick}
|
||||
onSeriesColorChange={onSeriesColorChange}
|
||||
>
|
||||
<TooltipPlugin mode={options.tooltipOptions.mode as any} timeZone={timeZone} />
|
||||
<ZoomPlugin onZoom={onChangeTimeRange} />
|
||||
|
@ -0,0 +1,151 @@
|
||||
import { FieldColorModeId, FieldConfigSource, FieldMatcherID } from '@grafana/data';
|
||||
import { changeSeriesColorConfigFactory } from './colorSeriesConfigFactory';
|
||||
|
||||
describe('changeSeriesColorConfigFactory', () => {
|
||||
it('should create config override to change color for serie', () => {
|
||||
const label = 'temperature';
|
||||
const color = 'green';
|
||||
|
||||
const existingConfig: FieldConfigSource = {
|
||||
defaults: {},
|
||||
overrides: [],
|
||||
};
|
||||
|
||||
const config = changeSeriesColorConfigFactory(label, color, existingConfig);
|
||||
|
||||
expect(config).toEqual({
|
||||
defaults: {},
|
||||
overrides: [
|
||||
{
|
||||
matcher: {
|
||||
id: FieldMatcherID.byName,
|
||||
options: label,
|
||||
},
|
||||
properties: [
|
||||
{
|
||||
id: 'color',
|
||||
value: {
|
||||
mode: FieldColorModeId.Fixed,
|
||||
fixedColor: color,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
it('should create config override to change color for serie when override already exists for series', () => {
|
||||
const label = 'temperature';
|
||||
const color = 'green';
|
||||
|
||||
const existingConfig: FieldConfigSource = {
|
||||
defaults: {},
|
||||
overrides: [
|
||||
{
|
||||
matcher: {
|
||||
id: FieldMatcherID.byName,
|
||||
options: label,
|
||||
},
|
||||
properties: [
|
||||
{
|
||||
id: 'other',
|
||||
value: 'other',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const config = changeSeriesColorConfigFactory(label, color, existingConfig);
|
||||
|
||||
expect(config).toEqual({
|
||||
defaults: {},
|
||||
overrides: [
|
||||
{
|
||||
matcher: {
|
||||
id: FieldMatcherID.byName,
|
||||
options: label,
|
||||
},
|
||||
properties: [
|
||||
{
|
||||
id: 'other',
|
||||
value: 'other',
|
||||
},
|
||||
{
|
||||
id: 'color',
|
||||
value: {
|
||||
mode: FieldColorModeId.Fixed,
|
||||
fixedColor: color,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
it('should create config override to change color for serie when override exists for other series', () => {
|
||||
const label = 'temperature';
|
||||
const color = 'green';
|
||||
|
||||
const existingConfig: FieldConfigSource = {
|
||||
defaults: {},
|
||||
overrides: [
|
||||
{
|
||||
matcher: {
|
||||
id: FieldMatcherID.byName,
|
||||
options: 'humidity',
|
||||
},
|
||||
properties: [
|
||||
{
|
||||
id: 'color',
|
||||
value: {
|
||||
mode: FieldColorModeId.Fixed,
|
||||
fixedColor: color,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const config = changeSeriesColorConfigFactory(label, color, existingConfig);
|
||||
|
||||
expect(config).toEqual({
|
||||
defaults: {},
|
||||
overrides: [
|
||||
{
|
||||
matcher: {
|
||||
id: FieldMatcherID.byName,
|
||||
options: 'humidity',
|
||||
},
|
||||
properties: [
|
||||
{
|
||||
id: 'color',
|
||||
value: {
|
||||
mode: FieldColorModeId.Fixed,
|
||||
fixedColor: color,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
matcher: {
|
||||
id: FieldMatcherID.byName,
|
||||
options: label,
|
||||
},
|
||||
properties: [
|
||||
{
|
||||
id: 'color',
|
||||
value: {
|
||||
mode: FieldColorModeId.Fixed,
|
||||
fixedColor: color,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
});
|
@ -0,0 +1,74 @@
|
||||
import {
|
||||
ConfigOverrideRule,
|
||||
DynamicConfigValue,
|
||||
FieldColorModeId,
|
||||
FieldConfigSource,
|
||||
FieldMatcherID,
|
||||
} from '@grafana/data';
|
||||
|
||||
export const changeSeriesColorConfigFactory = (
|
||||
label: string,
|
||||
color: string,
|
||||
fieldConfig: FieldConfigSource
|
||||
): FieldConfigSource => {
|
||||
const { overrides } = fieldConfig;
|
||||
const currentIndex = fieldConfig.overrides.findIndex(override => {
|
||||
return override.matcher.id === FieldMatcherID.byName && override.matcher.options === label;
|
||||
});
|
||||
|
||||
if (currentIndex < 0) {
|
||||
return {
|
||||
...fieldConfig,
|
||||
overrides: [...fieldConfig.overrides, createOverride(label, color)],
|
||||
};
|
||||
}
|
||||
|
||||
const overridesCopy = Array.from(overrides);
|
||||
const existing = overridesCopy[currentIndex];
|
||||
const propertyIndex = existing.properties.findIndex(p => p.id === 'color');
|
||||
|
||||
if (propertyIndex < 0) {
|
||||
overridesCopy[currentIndex] = {
|
||||
...existing,
|
||||
properties: [...existing.properties, createProperty(color)],
|
||||
};
|
||||
|
||||
return {
|
||||
...fieldConfig,
|
||||
overrides: overridesCopy,
|
||||
};
|
||||
}
|
||||
|
||||
const propertiesCopy = Array.from(existing.properties);
|
||||
propertiesCopy[propertyIndex] = createProperty(color);
|
||||
|
||||
overridesCopy[currentIndex] = {
|
||||
...existing,
|
||||
properties: propertiesCopy,
|
||||
};
|
||||
|
||||
return {
|
||||
...fieldConfig,
|
||||
overrides: overridesCopy,
|
||||
};
|
||||
};
|
||||
|
||||
const createOverride = (label: string, color: string): ConfigOverrideRule => {
|
||||
return {
|
||||
matcher: {
|
||||
id: FieldMatcherID.byName,
|
||||
options: label,
|
||||
},
|
||||
properties: [createProperty(color)],
|
||||
};
|
||||
};
|
||||
|
||||
const createProperty = (color: string): DynamicConfigValue => {
|
||||
return {
|
||||
id: 'color',
|
||||
value: {
|
||||
mode: FieldColorModeId.Fixed,
|
||||
fixedColor: color,
|
||||
},
|
||||
};
|
||||
};
|
@ -2,8 +2,9 @@ import React, { useCallback, useMemo } from 'react';
|
||||
import { Button, TooltipPlugin, GraphNG, GraphNGLegendEvent } from '@grafana/ui';
|
||||
import { PanelProps } from '@grafana/data';
|
||||
import { Options } from './types';
|
||||
import { hideSeriesConfigFactory } from '../timeseries/hideSeriesConfigFactory';
|
||||
import { hideSeriesConfigFactory } from '../timeseries/overrides/hideSeriesConfigFactory';
|
||||
import { getXYDimensions } from './dims';
|
||||
import { changeSeriesColorConfigFactory } from '../timeseries/overrides/colorSeriesConfigFactory';
|
||||
|
||||
interface XYChartPanelProps extends PanelProps<Options> {}
|
||||
|
||||
@ -41,6 +42,13 @@ export const XYChartPanel: React.FC<XYChartPanelProps> = ({
|
||||
[fieldConfig, onFieldConfigChange, frames]
|
||||
);
|
||||
|
||||
const onSeriesColorChange = useCallback(
|
||||
(label: string, color: string) => {
|
||||
onFieldConfigChange(changeSeriesColorConfigFactory(label, color, fieldConfig));
|
||||
},
|
||||
[fieldConfig, onFieldConfigChange]
|
||||
);
|
||||
|
||||
return (
|
||||
<GraphNG
|
||||
data={frames}
|
||||
@ -51,6 +59,7 @@ export const XYChartPanel: React.FC<XYChartPanelProps> = ({
|
||||
height={height}
|
||||
legend={options.legend}
|
||||
onLegendClick={onLegendClick}
|
||||
onSeriesColorChange={onSeriesColorChange}
|
||||
>
|
||||
<TooltipPlugin mode={options.tooltipOptions.mode as any} timeZone={timeZone} />
|
||||
<>{/* needs to be an array */}</>
|
||||
|
Loading…
Reference in New Issue
Block a user