mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Colors: default colors for strings and boolean (#33965)
This commit is contained in:
parent
765fa675f5
commit
ca8f6addab
@ -8,10 +8,10 @@ import { getValueFormat } from '../valueFormats/valueFormats';
|
||||
import { getValueMappingResult } from '../utils/valueMappings';
|
||||
import { dateTime } from '../datetime';
|
||||
import { KeyValue, TimeZone } from '../types';
|
||||
import { getScaleCalculator } from './scale';
|
||||
import { getScaleCalculator, ScaleCalculator } from './scale';
|
||||
import { GrafanaTheme2 } from '../themes/types';
|
||||
import { anyToNumber } from '../utils/anyToNumber';
|
||||
import { getColorForTheme } from '../utils/namedColorsPalette';
|
||||
import { classicColors, getColorForTheme } from '../utils/namedColorsPalette';
|
||||
|
||||
interface DisplayProcessorOptions {
|
||||
field: Partial<Field>;
|
||||
@ -41,7 +41,7 @@ export function getDisplayProcessor(options?: DisplayProcessorOptions): DisplayP
|
||||
return toStringProcessor;
|
||||
}
|
||||
|
||||
const { field } = options;
|
||||
const field = options.field as Field;
|
||||
const config = field.config ?? {};
|
||||
|
||||
let unit = config.unit;
|
||||
@ -53,7 +53,8 @@ export function getDisplayProcessor(options?: DisplayProcessorOptions): DisplayP
|
||||
}
|
||||
|
||||
const formatFunc = getValueFormat(unit || 'none');
|
||||
const scaleFunc = getScaleCalculator(field as Field, options.theme);
|
||||
const scaleFunc = getScaleCalculator(field, options.theme);
|
||||
const defaultColor = getDefaultColorFunc(field, scaleFunc, options.theme);
|
||||
|
||||
return (value: any) => {
|
||||
const { mappings } = config;
|
||||
@ -113,7 +114,7 @@ export function getDisplayProcessor(options?: DisplayProcessorOptions): DisplayP
|
||||
}
|
||||
|
||||
if (!color) {
|
||||
const scaleResult = scaleFunc(-Infinity);
|
||||
const scaleResult = defaultColor(value);
|
||||
color = scaleResult.color;
|
||||
percent = scaleResult.percent;
|
||||
}
|
||||
@ -132,3 +133,24 @@ export function getRawDisplayProcessor(): DisplayProcessor {
|
||||
numeric: (null as unknown) as number,
|
||||
});
|
||||
}
|
||||
|
||||
function getDefaultColorFunc(field: Field, scaleFunc: ScaleCalculator, theme: GrafanaTheme2) {
|
||||
if (field.type === FieldType.string) {
|
||||
return (value: any) => {
|
||||
const hc = strHashCode(value as string);
|
||||
return {
|
||||
color: classicColors[Math.floor(hc % classicColors.length)],
|
||||
percent: 0,
|
||||
};
|
||||
};
|
||||
}
|
||||
return (value: any) => scaleFunc(-Infinity);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a string into a numeric value -- we just need it to be different
|
||||
* enough so that it has a reasonable distribution across a color pallet
|
||||
*/
|
||||
function strHashCode(str: string) {
|
||||
return str.split('').reduce((prevHash, currVal) => ((prevHash << 5) - prevHash + currVal.charCodeAt(0)) | 0, 0);
|
||||
}
|
||||
|
@ -684,7 +684,7 @@ describe('applyRawFieldOverrides', () => {
|
||||
});
|
||||
|
||||
expect(getDisplayValue(frames, frameIndex, 3)).toEqual({
|
||||
color: '#808080',
|
||||
color: '#F2495C', // red
|
||||
numeric: 0,
|
||||
percent: expect.any(Number),
|
||||
prefix: undefined,
|
||||
@ -693,9 +693,9 @@ describe('applyRawFieldOverrides', () => {
|
||||
});
|
||||
|
||||
expect(getDisplayValue(frames, frameIndex, 4)).toEqual({
|
||||
color: '#808080',
|
||||
color: '#73BF69', // value from classic pallet
|
||||
numeric: NaN,
|
||||
percent: 0,
|
||||
percent: 1,
|
||||
prefix: undefined,
|
||||
suffix: undefined,
|
||||
text: 'A - string',
|
||||
|
@ -3,6 +3,7 @@ import { sortThresholds } from './thresholds';
|
||||
import { ArrayVector } from '../vector/ArrayVector';
|
||||
import { getScaleCalculator } from './scale';
|
||||
import { createTheme } from '../themes';
|
||||
import { getColorForTheme } from '../utils';
|
||||
|
||||
describe('getScaleCalculator', () => {
|
||||
it('should return percent, threshold and color', () => {
|
||||
@ -26,4 +27,26 @@ describe('getScaleCalculator', () => {
|
||||
color: '#EAB839',
|
||||
});
|
||||
});
|
||||
|
||||
it('reasonable boolean values', () => {
|
||||
const field: Field = {
|
||||
name: 'test',
|
||||
config: {},
|
||||
type: FieldType.boolean,
|
||||
values: new ArrayVector([true, false, true]),
|
||||
};
|
||||
|
||||
const theme = createTheme();
|
||||
const calc = getScaleCalculator(field, theme);
|
||||
expect(calc(true as any)).toEqual({
|
||||
percent: 1,
|
||||
color: getColorForTheme('green', theme.v1),
|
||||
threshold: undefined,
|
||||
});
|
||||
expect(calc(false as any)).toEqual({
|
||||
percent: 0,
|
||||
color: getColorForTheme('red', theme.v1),
|
||||
threshold: undefined,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -2,6 +2,7 @@ import { isNumber } from 'lodash';
|
||||
import { GrafanaTheme2 } from '../themes/types';
|
||||
import { reduceField, ReducerID } from '../transformations/fieldReducer';
|
||||
import { Field, FieldConfig, FieldType, NumericRange, Threshold } from '../types';
|
||||
import { getColorForTheme } from '../utils';
|
||||
import { getFieldColorModeForField } from './fieldColor';
|
||||
import { getActiveThresholdForValue } from './thresholds';
|
||||
|
||||
@ -14,6 +15,10 @@ export interface ColorScaleValue {
|
||||
export type ScaleCalculator = (value: number) => ColorScaleValue;
|
||||
|
||||
export function getScaleCalculator(field: Field, theme: GrafanaTheme2): ScaleCalculator {
|
||||
if (field.type === FieldType.boolean) {
|
||||
return getBooleanScaleCalculator(field, theme);
|
||||
}
|
||||
|
||||
const mode = getFieldColorModeForField(field);
|
||||
const getColor = mode.getCalculator(field, theme);
|
||||
const info = field.state?.range ?? getMinMaxAndDelta(field);
|
||||
@ -35,6 +40,30 @@ export function getScaleCalculator(field: Field, theme: GrafanaTheme2): ScaleCal
|
||||
};
|
||||
}
|
||||
|
||||
function getBooleanScaleCalculator(field: Field, theme: GrafanaTheme2): ScaleCalculator {
|
||||
const trueValue: ColorScaleValue = {
|
||||
color: getColorForTheme('green', theme.v1),
|
||||
percent: 1,
|
||||
threshold: (undefined as unknown) as Threshold,
|
||||
};
|
||||
|
||||
const falseValue: ColorScaleValue = {
|
||||
color: getColorForTheme('red', theme.v1),
|
||||
percent: 0,
|
||||
threshold: (undefined as unknown) as Threshold,
|
||||
};
|
||||
|
||||
const mode = getFieldColorModeForField(field);
|
||||
if (mode.isContinuous && mode.colors) {
|
||||
trueValue.color = getColorForTheme(mode.colors[mode.colors.length - 1], theme.v1);
|
||||
falseValue.color = getColorForTheme(mode.colors[0], theme.v1);
|
||||
}
|
||||
|
||||
return (value: number) => {
|
||||
return Boolean(value) ? trueValue : falseValue;
|
||||
};
|
||||
}
|
||||
|
||||
function getMinMaxAndDelta(field: Field): NumericRange {
|
||||
if (field.type !== FieldType.number) {
|
||||
return { min: 0, max: 100, delta: 100 };
|
||||
|
Loading…
Reference in New Issue
Block a user