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 { getValueMappingResult } from '../utils/valueMappings';
|
||||||
import { dateTime } from '../datetime';
|
import { dateTime } from '../datetime';
|
||||||
import { KeyValue, TimeZone } from '../types';
|
import { KeyValue, TimeZone } from '../types';
|
||||||
import { getScaleCalculator } from './scale';
|
import { getScaleCalculator, ScaleCalculator } from './scale';
|
||||||
import { GrafanaTheme2 } from '../themes/types';
|
import { GrafanaTheme2 } from '../themes/types';
|
||||||
import { anyToNumber } from '../utils/anyToNumber';
|
import { anyToNumber } from '../utils/anyToNumber';
|
||||||
import { getColorForTheme } from '../utils/namedColorsPalette';
|
import { classicColors, getColorForTheme } from '../utils/namedColorsPalette';
|
||||||
|
|
||||||
interface DisplayProcessorOptions {
|
interface DisplayProcessorOptions {
|
||||||
field: Partial<Field>;
|
field: Partial<Field>;
|
||||||
@ -41,7 +41,7 @@ export function getDisplayProcessor(options?: DisplayProcessorOptions): DisplayP
|
|||||||
return toStringProcessor;
|
return toStringProcessor;
|
||||||
}
|
}
|
||||||
|
|
||||||
const { field } = options;
|
const field = options.field as Field;
|
||||||
const config = field.config ?? {};
|
const config = field.config ?? {};
|
||||||
|
|
||||||
let unit = config.unit;
|
let unit = config.unit;
|
||||||
@ -53,7 +53,8 @@ export function getDisplayProcessor(options?: DisplayProcessorOptions): DisplayP
|
|||||||
}
|
}
|
||||||
|
|
||||||
const formatFunc = getValueFormat(unit || 'none');
|
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) => {
|
return (value: any) => {
|
||||||
const { mappings } = config;
|
const { mappings } = config;
|
||||||
@ -113,7 +114,7 @@ export function getDisplayProcessor(options?: DisplayProcessorOptions): DisplayP
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!color) {
|
if (!color) {
|
||||||
const scaleResult = scaleFunc(-Infinity);
|
const scaleResult = defaultColor(value);
|
||||||
color = scaleResult.color;
|
color = scaleResult.color;
|
||||||
percent = scaleResult.percent;
|
percent = scaleResult.percent;
|
||||||
}
|
}
|
||||||
@ -132,3 +133,24 @@ export function getRawDisplayProcessor(): DisplayProcessor {
|
|||||||
numeric: (null as unknown) as number,
|
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({
|
expect(getDisplayValue(frames, frameIndex, 3)).toEqual({
|
||||||
color: '#808080',
|
color: '#F2495C', // red
|
||||||
numeric: 0,
|
numeric: 0,
|
||||||
percent: expect.any(Number),
|
percent: expect.any(Number),
|
||||||
prefix: undefined,
|
prefix: undefined,
|
||||||
@ -693,9 +693,9 @@ describe('applyRawFieldOverrides', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
expect(getDisplayValue(frames, frameIndex, 4)).toEqual({
|
expect(getDisplayValue(frames, frameIndex, 4)).toEqual({
|
||||||
color: '#808080',
|
color: '#73BF69', // value from classic pallet
|
||||||
numeric: NaN,
|
numeric: NaN,
|
||||||
percent: 0,
|
percent: 1,
|
||||||
prefix: undefined,
|
prefix: undefined,
|
||||||
suffix: undefined,
|
suffix: undefined,
|
||||||
text: 'A - string',
|
text: 'A - string',
|
||||||
|
@ -3,6 +3,7 @@ import { sortThresholds } from './thresholds';
|
|||||||
import { ArrayVector } from '../vector/ArrayVector';
|
import { ArrayVector } from '../vector/ArrayVector';
|
||||||
import { getScaleCalculator } from './scale';
|
import { getScaleCalculator } from './scale';
|
||||||
import { createTheme } from '../themes';
|
import { createTheme } from '../themes';
|
||||||
|
import { getColorForTheme } from '../utils';
|
||||||
|
|
||||||
describe('getScaleCalculator', () => {
|
describe('getScaleCalculator', () => {
|
||||||
it('should return percent, threshold and color', () => {
|
it('should return percent, threshold and color', () => {
|
||||||
@ -26,4 +27,26 @@ describe('getScaleCalculator', () => {
|
|||||||
color: '#EAB839',
|
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 { GrafanaTheme2 } from '../themes/types';
|
||||||
import { reduceField, ReducerID } from '../transformations/fieldReducer';
|
import { reduceField, ReducerID } from '../transformations/fieldReducer';
|
||||||
import { Field, FieldConfig, FieldType, NumericRange, Threshold } from '../types';
|
import { Field, FieldConfig, FieldType, NumericRange, Threshold } from '../types';
|
||||||
|
import { getColorForTheme } from '../utils';
|
||||||
import { getFieldColorModeForField } from './fieldColor';
|
import { getFieldColorModeForField } from './fieldColor';
|
||||||
import { getActiveThresholdForValue } from './thresholds';
|
import { getActiveThresholdForValue } from './thresholds';
|
||||||
|
|
||||||
@ -14,6 +15,10 @@ export interface ColorScaleValue {
|
|||||||
export type ScaleCalculator = (value: number) => ColorScaleValue;
|
export type ScaleCalculator = (value: number) => ColorScaleValue;
|
||||||
|
|
||||||
export function getScaleCalculator(field: Field, theme: GrafanaTheme2): ScaleCalculator {
|
export function getScaleCalculator(field: Field, theme: GrafanaTheme2): ScaleCalculator {
|
||||||
|
if (field.type === FieldType.boolean) {
|
||||||
|
return getBooleanScaleCalculator(field, theme);
|
||||||
|
}
|
||||||
|
|
||||||
const mode = getFieldColorModeForField(field);
|
const mode = getFieldColorModeForField(field);
|
||||||
const getColor = mode.getCalculator(field, theme);
|
const getColor = mode.getCalculator(field, theme);
|
||||||
const info = field.state?.range ?? getMinMaxAndDelta(field);
|
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 {
|
function getMinMaxAndDelta(field: Field): NumericRange {
|
||||||
if (field.type !== FieldType.number) {
|
if (field.type !== FieldType.number) {
|
||||||
return { min: 0, max: 100, delta: 100 };
|
return { min: 0, max: 100, delta: 100 };
|
||||||
|
Loading…
Reference in New Issue
Block a user