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 |
|
| Color mode | Description |
|
||||||
| ------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
| ------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||||
| **Single color** | Specify a single color, useful in an override rule |
|
| **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 |
|
| **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 |
|
| **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 |
|
| **Green-Yellow-Red (by value)** | Continuous color scheme |
|
||||||
|
@ -4,7 +4,7 @@ import { ArrayVector } from '../vector/ArrayVector';
|
|||||||
|
|
||||||
import { fieldColorModeRegistry, FieldValueColorCalculator, getFieldSeriesColor } from './fieldColor';
|
import { fieldColorModeRegistry, FieldValueColorCalculator, getFieldSeriesColor } from './fieldColor';
|
||||||
|
|
||||||
function getTestField(mode: string): Field {
|
function getTestField(mode: string, fixedColor?: string): Field {
|
||||||
return {
|
return {
|
||||||
name: 'name',
|
name: 'name',
|
||||||
type: FieldType.number,
|
type: FieldType.number,
|
||||||
@ -12,6 +12,7 @@ function getTestField(mode: string): Field {
|
|||||||
config: {
|
config: {
|
||||||
color: {
|
color: {
|
||||||
mode: mode,
|
mode: mode,
|
||||||
|
fixedColor: fixedColor,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
state: {},
|
state: {},
|
||||||
@ -21,10 +22,11 @@ function getTestField(mode: string): Field {
|
|||||||
interface GetCalcOptions {
|
interface GetCalcOptions {
|
||||||
mode: string;
|
mode: string;
|
||||||
seriesIndex?: number;
|
seriesIndex?: number;
|
||||||
|
fixedColor?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getCalculator(options: GetCalcOptions): FieldValueColorCalculator {
|
function getCalculator(options: GetCalcOptions): FieldValueColorCalculator {
|
||||||
const field = getTestField(options.mode);
|
const field = getTestField(options.mode, options.fixedColor);
|
||||||
const mode = fieldColorModeRegistry.get(options.mode);
|
const mode = fieldColorModeRegistry.get(options.mode);
|
||||||
field.state!.seriesIndex = options.seriesIndex;
|
field.state!.seriesIndex = options.seriesIndex;
|
||||||
return mode.getCalculator(field, createTheme());
|
return mode.getCalculator(field, createTheme());
|
||||||
@ -59,6 +61,18 @@ describe('fieldColorModeRegistry', () => {
|
|||||||
|
|
||||||
expect(color.color).toEqual(calcFn(4, 0.75));
|
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', () => {
|
describe('getFieldSeriesColor', () => {
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import { interpolateRgbBasis } from 'd3-interpolate';
|
import { interpolateRgbBasis } from 'd3-interpolate';
|
||||||
|
import tinycolor from 'tinycolor2';
|
||||||
|
|
||||||
import { GrafanaTheme2 } from '../themes/types';
|
import { GrafanaTheme2 } from '../themes/types';
|
||||||
import { reduceField } from '../transformations/fieldReducer';
|
import { reduceField } from '../transformations/fieldReducer';
|
||||||
@ -29,6 +30,12 @@ export const fieldColorModeRegistry = new Registry<FieldColorMode>(() => {
|
|||||||
description: 'Set a specific color',
|
description: 'Set a specific color',
|
||||||
getCalculator: getFixedColor,
|
getCalculator: getFixedColor,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
id: FieldColorModeId.Shades,
|
||||||
|
name: 'Shades of a color',
|
||||||
|
description: 'Select shades of a specific color',
|
||||||
|
getCalculator: getShadedColor,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
id: FieldColorModeId.Thresholds,
|
id: FieldColorModeId.Thresholds,
|
||||||
name: 'From thresholds',
|
name: 'From thresholds',
|
||||||
@ -236,3 +243,39 @@ function getFixedColor(field: Field, theme: GrafanaTheme2) {
|
|||||||
return theme.visualization.getColorByName(field.config.color?.fixedColor ?? FALLBACK_COLOR);
|
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',
|
ContinuousGreens = 'continuous-greens',
|
||||||
ContinuousPurples = 'continuous-purples',
|
ContinuousPurples = 'continuous-purples',
|
||||||
Fixed = 'fixed',
|
Fixed = 'fixed',
|
||||||
|
Shades = 'shades',
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -68,7 +68,7 @@ export const FieldColorEditor = ({ value, onChange, item, id }: Props) => {
|
|||||||
|
|
||||||
const mode = value?.mode ?? FieldColorModeId.Thresholds;
|
const mode = value?.mode ?? FieldColorModeId.Thresholds;
|
||||||
|
|
||||||
if (mode === FieldColorModeId.Fixed) {
|
if (mode === FieldColorModeId.Fixed || mode === FieldColorModeId.Shades) {
|
||||||
return (
|
return (
|
||||||
<div className={styles.group}>
|
<div className={styles.group}>
|
||||||
<Select
|
<Select
|
||||||
|
Loading…
Reference in New Issue
Block a user