mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Dashboard: Add series color shades (#61300)
* Dashboard: Add series color shades Add color option "Shades of a color" which gives each series a color derived from a user-selected base color. * Documentation: Add entry for color shades Describe color option "Shades of a color" in documentation. * Chore: formatting fixes * Dashboard: Use better fallback color for color shades in fieldColor.ts Fall back to a gray color if the configured color cannot be parsed. * Chore: fix typo in fieldColor.test.ts * Documentation: fix a sentence * remove custom color parsing and change logic a bit * Fix prettier issue --------- Co-authored-by: Torkel Ödegaard <torkel@grafana.com> Co-authored-by: nmarrs <nathanielmarrs@gmail.com> Co-authored-by: Kristina Durivage <kristina.durivage@grafana.com>
This commit is contained in:
parent
4a7f27489e
commit
8d6314c654
@ -119,6 +119,7 @@ Select one of the following palettes:
|
||||
| Color mode | Description |
|
||||
| ------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| **Single color** | Specify a single color, useful in an override rule |
|
||||
| **Shades of a color** | Selects shades of a single color, useful in an override rule |
|
||||
| **From thresholds** | Informs Grafana to take the color from the matching threshold |
|
||||
| **Classic palette** | Grafana will assign color by looking up a color in a palette by series index. Useful for Graphs and pie charts and other categorical data visualizations |
|
||||
| **Green-Yellow-Red (by value)** | Continuous color scheme |
|
||||
|
@ -4,7 +4,7 @@ import { ArrayVector } from '../vector/ArrayVector';
|
||||
|
||||
import { fieldColorModeRegistry, FieldValueColorCalculator, getFieldSeriesColor } from './fieldColor';
|
||||
|
||||
function getTestField(mode: string): Field {
|
||||
function getTestField(mode: string, fixedColor?: string): Field {
|
||||
return {
|
||||
name: 'name',
|
||||
type: FieldType.number,
|
||||
@ -12,6 +12,7 @@ function getTestField(mode: string): Field {
|
||||
config: {
|
||||
color: {
|
||||
mode: mode,
|
||||
fixedColor: fixedColor,
|
||||
},
|
||||
},
|
||||
state: {},
|
||||
@ -21,10 +22,11 @@ function getTestField(mode: string): Field {
|
||||
interface GetCalcOptions {
|
||||
mode: string;
|
||||
seriesIndex?: number;
|
||||
fixedColor?: string;
|
||||
}
|
||||
|
||||
function getCalculator(options: GetCalcOptions): FieldValueColorCalculator {
|
||||
const field = getTestField(options.mode);
|
||||
const field = getTestField(options.mode, options.fixedColor);
|
||||
const mode = fieldColorModeRegistry.get(options.mode);
|
||||
field.state!.seriesIndex = options.seriesIndex;
|
||||
return mode.getCalculator(field, createTheme());
|
||||
@ -59,6 +61,18 @@ describe('fieldColorModeRegistry', () => {
|
||||
|
||||
expect(color.color).toEqual(calcFn(4, 0.75));
|
||||
});
|
||||
|
||||
it('Shades should return selected color for index 0', () => {
|
||||
const color = '#123456';
|
||||
const calcFn = getCalculator({ mode: FieldColorModeId.Shades, seriesIndex: 0, fixedColor: color });
|
||||
expect(calcFn(70, 0, undefined)).toEqual(color);
|
||||
});
|
||||
|
||||
it('Shades should return different than selected color for index 1', () => {
|
||||
const color = '#123456';
|
||||
const calcFn = getCalculator({ mode: FieldColorModeId.Shades, seriesIndex: 1, fixedColor: color });
|
||||
expect(calcFn(70, 0, undefined)).not.toEqual(color);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getFieldSeriesColor', () => {
|
||||
|
@ -1,4 +1,5 @@
|
||||
import { interpolateRgbBasis } from 'd3-interpolate';
|
||||
import tinycolor from 'tinycolor2';
|
||||
|
||||
import { GrafanaTheme2 } from '../themes/types';
|
||||
import { reduceField } from '../transformations/fieldReducer';
|
||||
@ -29,6 +30,12 @@ export const fieldColorModeRegistry = new Registry<FieldColorMode>(() => {
|
||||
description: 'Set a specific color',
|
||||
getCalculator: getFixedColor,
|
||||
},
|
||||
{
|
||||
id: FieldColorModeId.Shades,
|
||||
name: 'Shades of a color',
|
||||
description: 'Select shades of a specific color',
|
||||
getCalculator: getShadedColor,
|
||||
},
|
||||
{
|
||||
id: FieldColorModeId.Thresholds,
|
||||
name: 'From thresholds',
|
||||
@ -236,3 +243,39 @@ function getFixedColor(field: Field, theme: GrafanaTheme2) {
|
||||
return theme.visualization.getColorByName(field.config.color?.fixedColor ?? FALLBACK_COLOR);
|
||||
};
|
||||
}
|
||||
|
||||
function getShadedColor(field: Field, theme: GrafanaTheme2) {
|
||||
return () => {
|
||||
const baseColorString: string = theme.visualization.getColorByName(
|
||||
field.config.color?.fixedColor ?? FALLBACK_COLOR
|
||||
);
|
||||
|
||||
const colors: string[] = [
|
||||
baseColorString, // start with base color
|
||||
];
|
||||
|
||||
const shadesCount = 6;
|
||||
const maxHueSpin = 10; // hue spin, max is 360
|
||||
const maxDarken = 35; // max 100%
|
||||
const maxBrighten = 35; // max 100%
|
||||
|
||||
for (let i = 1; i < shadesCount; i++) {
|
||||
// push alternating darker and brighter shades
|
||||
colors.push(
|
||||
tinycolor(baseColorString)
|
||||
.spin((i / shadesCount) * maxHueSpin)
|
||||
.brighten((i / shadesCount) * maxDarken)
|
||||
.toHexString()
|
||||
);
|
||||
colors.push(
|
||||
tinycolor(baseColorString)
|
||||
.spin(-(i / shadesCount) * maxHueSpin)
|
||||
.darken((i / shadesCount) * maxBrighten)
|
||||
.toHexString()
|
||||
);
|
||||
}
|
||||
|
||||
const seriesIndex = field.state?.seriesIndex ?? 0;
|
||||
return colors[seriesIndex % colors.length];
|
||||
};
|
||||
}
|
||||
|
@ -16,6 +16,7 @@ export enum FieldColorModeId {
|
||||
ContinuousGreens = 'continuous-greens',
|
||||
ContinuousPurples = 'continuous-purples',
|
||||
Fixed = 'fixed',
|
||||
Shades = 'shades',
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -68,7 +68,7 @@ export const FieldColorEditor = ({ value, onChange, item, id }: Props) => {
|
||||
|
||||
const mode = value?.mode ?? FieldColorModeId.Thresholds;
|
||||
|
||||
if (mode === FieldColorModeId.Fixed) {
|
||||
if (mode === FieldColorModeId.Fixed || mode === FieldColorModeId.Shades) {
|
||||
return (
|
||||
<div className={styles.group}>
|
||||
<Select
|
||||
|
Loading…
Reference in New Issue
Block a user