Visualizations: Choose color based on series name (#66197)

This commit is contained in:
Luke Palmer
2023-04-14 23:08:28 -07:00
committed by GitHub
parent 39c04a8e36
commit 0181dc183b
6 changed files with 63 additions and 17 deletions
+2
View File
@@ -38,6 +38,7 @@
"@braintree/sanitize-url": "6.0.2",
"@grafana/schema": "10.0.0-pre",
"@types/d3-interpolate": "^3.0.0",
"@types/string-hash": "1.1.1",
"d3-interpolate": "3.0.1",
"date-fns": "2.29.3",
"dompurify": "^2.4.3",
@@ -53,6 +54,7 @@
"react-use": "17.4.0",
"regenerator-runtime": "0.13.11",
"rxjs": "7.8.0",
"string-hash": "^1.1.3",
"tinycolor2": "1.6.0",
"tslib": "2.5.0",
"uplot": "1.6.24",
@@ -3,9 +3,9 @@ import { Field, FieldColorModeId, FieldType } from '../types';
import { fieldColorModeRegistry, FieldValueColorCalculator, getFieldSeriesColor } from './fieldColor';
function getTestField(mode: string, fixedColor?: string): Field {
function getTestField(mode: string, fixedColor?: string, name = 'name'): Field {
return {
name: 'name',
name: name,
type: FieldType.number,
values: [],
config: {
@@ -21,11 +21,12 @@ function getTestField(mode: string, fixedColor?: string): Field {
interface GetCalcOptions {
mode: string;
seriesIndex?: number;
name?: string;
fixedColor?: string;
}
function getCalculator(options: GetCalcOptions): FieldValueColorCalculator {
const field = getTestField(options.mode, options.fixedColor);
const field = getTestField(options.mode, options.fixedColor, options.name);
const mode = fieldColorModeRegistry.get(options.mode);
field.state!.seriesIndex = options.seriesIndex;
return mode.getCalculator(field, createTheme());
@@ -38,15 +39,21 @@ describe('fieldColorModeRegistry', () => {
});
it('Palette classic with series index 0', () => {
const calcFn = getCalculator({ mode: FieldColorModeId.PaletteClassic, seriesIndex: 0 });
const calcFn = getCalculator({ mode: FieldColorModeId.PaletteClassic, seriesIndex: 0, name: 'series1' });
expect(calcFn(70, 0, undefined)).toEqual('#73BF69');
});
it('Palette classic with series index 1', () => {
const calcFn = getCalculator({ mode: FieldColorModeId.PaletteClassic, seriesIndex: 1 });
const calcFn = getCalculator({ mode: FieldColorModeId.PaletteClassic, seriesIndex: 1, name: 'series2' });
expect(calcFn(70, 0, undefined)).toEqual('#F2CC0C');
});
it('Palette uses name', () => {
const calcFn1 = getCalculator({ mode: FieldColorModeId.PaletteClassicByName, seriesIndex: 0, name: 'same name' });
const calcFn2 = getCalculator({ mode: FieldColorModeId.PaletteClassicByName, seriesIndex: 1, name: 'same name' });
expect(calcFn1(12, 34, undefined)).toEqual(calcFn2(56, 78, undefined));
});
it('When color.seriesBy is set to last use that instead of v', () => {
const field = getTestField('continuous-GrYlRd');
@@ -1,6 +1,8 @@
import { interpolateRgbBasis } from 'd3-interpolate';
import stringHash from 'string-hash';
import tinycolor from 'tinycolor2';
import { colorManipulator } from '../themes';
import { GrafanaTheme2 } from '../themes/types';
import { reduceField } from '../transformations/fieldReducer';
import { FALLBACK_COLOR, Field, FieldColorModeId, Threshold } from '../types';
@@ -19,6 +21,7 @@ export interface FieldColorMode extends RegistryItem {
getColors?: (theme: GrafanaTheme2) => string[];
isContinuous?: boolean;
isByValue?: boolean;
useSeriesName?: boolean;
}
/** @internal */
@@ -57,6 +60,22 @@ export const fieldColorModeRegistry = new Registry<FieldColorMode>(() => {
return theme.visualization.palette;
},
}),
new FieldColorSchemeMode({
id: FieldColorModeId.PaletteClassicByName,
name: 'Classic palette (by series name)',
isContinuous: false,
isByValue: false,
useSeriesName: true,
getColors: (theme: GrafanaTheme2) => {
return theme.visualization.palette.filter(
(color) =>
colorManipulator.getContrastRatio(
theme.visualization.getColorByName(color),
theme.colors.background.primary
) >= theme.colors.contrastThreshold
);
},
}),
new FieldColorSchemeMode({
id: FieldColorModeId.ContinuousGrYlRd,
name: 'Green-Yellow-Red',
@@ -137,6 +156,7 @@ interface FieldColorSchemeModeOptions {
getColors: (theme: GrafanaTheme2) => string[];
isContinuous: boolean;
isByValue: boolean;
useSeriesName?: boolean;
}
export class FieldColorSchemeMode implements FieldColorMode {
@@ -145,6 +165,7 @@ export class FieldColorSchemeMode implements FieldColorMode {
description?: string;
isContinuous: boolean;
isByValue: boolean;
useSeriesName?: boolean;
colorCache?: string[];
colorCacheTheme?: GrafanaTheme2;
interpolator?: (value: number) => string;
@@ -157,6 +178,7 @@ export class FieldColorSchemeMode implements FieldColorMode {
this.getNamedColors = options.getColors;
this.isContinuous = options.isContinuous;
this.isByValue = options.isByValue;
this.useSeriesName = options.useSeriesName;
}
getColors(theme: GrafanaTheme2): string[] {
@@ -195,6 +217,10 @@ export class FieldColorSchemeMode implements FieldColorMode {
return colors[percent * (colors.length - 1)];
};
}
} else if (this.useSeriesName) {
return (_: number, _percent: number, _threshold?: Threshold) => {
return colors[Math.abs(stringHash(field.name)) % colors.length];
};
} else {
return (_: number, _percent: number, _threshold?: Threshold) => {
const seriesIndex = field.state?.seriesIndex ?? 0;
@@ -4,6 +4,7 @@
export enum FieldColorModeId {
Thresholds = 'thresholds',
PaletteClassic = 'palette-classic',
PaletteClassicByName = 'palette-classic-by-name',
PaletteSaturated = 'palette-saturated',
ContinuousGrYlRd = 'continuous-GrYlRd',
ContinuousRdYlGr = 'continuous-RdYlGr',