From 567bbdf6e647374cc9ea344e2fb2e4f954e53f42 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Thu, 13 May 2021 07:32:13 +0200 Subject: [PATCH] Theme: New visualization colors model (#33973) * Initial design for new viz colors * added unit tests * Progress * Updated selected color * Use old named color names and colors to begin with * Updates * Fixing tests * Progress * Updates * updating * fixing tests * Using some named colors * renames && fixes * remove plural * fixed tests --- .../src/field/displayProcessor.ts | 5 +- .../grafana-data/src/field/fieldColor.test.ts | 4 +- packages/grafana-data/src/field/fieldColor.ts | 71 +-- packages/grafana-data/src/field/scale.ts | 12 +- .../grafana-data/src/themes/createTheme.ts | 3 + .../grafana-data/src/themes/createV1Theme.ts | 1 + .../themes/createVisualizationColors.test.ts | 32 ++ .../src/themes/createVisualizationColors.ts | 521 ++++++++++++++++++ packages/grafana-data/src/themes/index.ts | 2 +- packages/grafana-data/src/themes/types.ts | 2 + packages/grafana-data/src/types/theme.ts | 3 + .../src/utils/namedColorsPalette.test.ts | 27 +- .../src/utils/namedColorsPalette.ts | 207 +------ .../src/components/BarGauge/BarGauge.tsx | 8 +- .../src/components/BigValue/BigValue.test.tsx | 1 + .../components/BigValue/BigValueLayout.tsx | 6 +- .../__snapshots__/BigValue.test.tsx.snap | 3 +- .../components/ColorPicker/ColorPicker.tsx | 4 +- .../ColorPicker/ColorPickerPopover.test.tsx | 22 +- .../ColorPicker/ColorPickerPopover.tsx | 4 +- .../ColorPicker/NamedColorsGroup.tsx | 44 +- .../ColorPicker/NamedColorsPalette.test.tsx | 25 +- .../ColorPicker/NamedColorsPalette.tsx | 17 +- .../src/components/OptionsUI/fieldColor.tsx | 19 +- .../src/themes/ThemeContext.test.tsx | 3 - .../app/plugins/panel/piechart/PieChart.tsx | 1 + 26 files changed, 673 insertions(+), 374 deletions(-) create mode 100644 packages/grafana-data/src/themes/createVisualizationColors.test.ts create mode 100644 packages/grafana-data/src/themes/createVisualizationColors.ts diff --git a/packages/grafana-data/src/field/displayProcessor.ts b/packages/grafana-data/src/field/displayProcessor.ts index 743e56d6155..6b1080772e2 100644 --- a/packages/grafana-data/src/field/displayProcessor.ts +++ b/packages/grafana-data/src/field/displayProcessor.ts @@ -11,7 +11,6 @@ import { KeyValue, TimeZone } from '../types'; import { getScaleCalculator, ScaleCalculator } from './scale'; import { GrafanaTheme2 } from '../themes/types'; import { anyToNumber } from '../utils/anyToNumber'; -import { classicColors, getColorForTheme } from '../utils/namedColorsPalette'; interface DisplayProcessorOptions { field: Partial; @@ -82,7 +81,7 @@ export function getDisplayProcessor(options?: DisplayProcessorOptions): DisplayP } if (mappingResult.color != null) { - color = getColorForTheme(mappingResult.color, options.theme.v1); + color = options.theme.visualization.getColorByName(mappingResult.color); } shouldFormat = false; @@ -143,7 +142,7 @@ function getDefaultColorFunc(field: Field, scaleFunc: ScaleCalculator, theme: Gr const hc = strHashCode(value as string); return { - color: classicColors[Math.floor(hc % classicColors.length)], + color: theme.visualization.palette[Math.floor(hc % theme.visualization.palette.length)], percent: 0, }; }; diff --git a/packages/grafana-data/src/field/fieldColor.test.ts b/packages/grafana-data/src/field/fieldColor.test.ts index 6509d8c2dcb..08eb1a0bbb7 100644 --- a/packages/grafana-data/src/field/fieldColor.test.ts +++ b/packages/grafana-data/src/field/fieldColor.test.ts @@ -37,12 +37,12 @@ describe('fieldColorModeRegistry', () => { it('Palette classic with series index 0', () => { const calcFn = getCalculator({ mode: FieldColorModeId.PaletteClassic, seriesIndex: 0 }); - expect(calcFn(70, 0, undefined)).toEqual('#7EB26D'); + expect(calcFn(70, 0, undefined)).toEqual('#73BF69'); }); it('Palette classic with series index 1', () => { const calcFn = getCalculator({ mode: FieldColorModeId.PaletteClassic, seriesIndex: 1 }); - expect(calcFn(70, 0, undefined)).toEqual('#EAB839'); + expect(calcFn(70, 0, undefined)).toEqual('#F2CC0C'); }); it('When color.seriesBy is set to last use that instead of v', () => { diff --git a/packages/grafana-data/src/field/fieldColor.ts b/packages/grafana-data/src/field/fieldColor.ts index e8d956350e4..4432301236a 100644 --- a/packages/grafana-data/src/field/fieldColor.ts +++ b/packages/grafana-data/src/field/fieldColor.ts @@ -1,5 +1,5 @@ import { FALLBACK_COLOR, Field, FieldColorModeId, Threshold } from '../types'; -import { classicColors, getColorForTheme, RegistryItem } from '../utils'; +import { RegistryItem } from '../utils'; import { Registry } from '../utils/Registry'; import { interpolateRgbBasis } from 'd3-interpolate'; import { fallBackTreshold } from './thresholds'; @@ -13,7 +13,7 @@ export type FieldValueColorCalculator = (value: number, percent: number, Thresho /** @beta */ export interface FieldColorMode extends RegistryItem { getCalculator: (field: Field, theme: GrafanaTheme2) => FieldValueColorCalculator; - colors?: string[]; + getColors?: (theme: GrafanaTheme2) => string[]; isContinuous?: boolean; isByValue?: boolean; } @@ -35,106 +35,88 @@ export const fieldColorModeRegistry = new Registry(() => { getCalculator: (_field, theme) => { return (_value, _percent, threshold) => { const thresholdSafe = threshold ?? fallBackTreshold; - return getColorForTheme(thresholdSafe.color, theme.v1); + return theme.visualization.getColorByName(thresholdSafe.color); }; }, }, - // new FieldColorSchemeMode({ - // id: FieldColorModeId.PaletteSaturated, - // name: 'Saturated palette', - // //description: 'Assigns color based on series or field index', - // isContinuous: false, - // isByValue: false, - // colors: [ - // 'blue', - // 'red', - // 'green', - // 'orange', - // 'purple', - // 'orange', - // 'dark-blue', - // 'dark-red', - // 'dark-yellow', - // 'dark-purple', - // 'dark-orange', - // ], - // }), new FieldColorSchemeMode({ id: FieldColorModeId.PaletteClassic, name: 'Classic palette', isContinuous: false, isByValue: false, - colors: classicColors, + getColors: (theme: GrafanaTheme2) => { + return theme.visualization.palette; + }, }), new FieldColorSchemeMode({ id: 'continuous-GrYlRd', name: 'Green-Yellow-Red', isContinuous: true, isByValue: true, - colors: ['green', 'yellow', 'red'], + getColors: (theme: GrafanaTheme2) => ['green', 'yellow', 'red'], }), new FieldColorSchemeMode({ id: 'continuous-RdYlGr', name: 'Red-Yellow-Green', isContinuous: true, isByValue: true, - colors: ['red', 'yellow', 'green'], + getColors: (theme: GrafanaTheme2) => ['red', 'yellow', 'green'], }), new FieldColorSchemeMode({ id: 'continuous-BlYlRd', name: 'Blue-Yellow-Red', isContinuous: true, isByValue: true, - colors: ['dark-blue', 'super-light-yellow', 'dark-red'], + getColors: (theme: GrafanaTheme2) => ['dark-blue', 'super-light-yellow', 'dark-red'], }), new FieldColorSchemeMode({ id: 'continuous-YlRd', name: 'Yellow-Red', isContinuous: true, isByValue: true, - colors: ['super-light-yellow', 'dark-red'], + getColors: (theme: GrafanaTheme2) => ['super-light-yellow', 'dark-red'], }), new FieldColorSchemeMode({ id: 'continuous-BlPu', name: 'Blue-Purple', isContinuous: true, isByValue: true, - colors: ['blue', 'purple'], + getColors: (theme: GrafanaTheme2) => ['blue', 'purple'], }), new FieldColorSchemeMode({ id: 'continuous-YlBl', name: 'Yellow-Blue', isContinuous: true, isByValue: true, - colors: ['super-light-yellow', 'dark-blue'], + getColors: (theme: GrafanaTheme2) => ['super-light-yellow', 'dark-blue'], }), new FieldColorSchemeMode({ id: 'continuous-blues', name: 'Blues', isContinuous: true, isByValue: true, - colors: ['panel-bg', 'dark-blue'], + getColors: (theme: GrafanaTheme2) => ['panel-bg', 'dark-blue'], }), new FieldColorSchemeMode({ id: 'continuous-reds', name: 'Reds', isContinuous: true, isByValue: true, - colors: ['panel-bg', 'dark-red'], + getColors: (theme: GrafanaTheme2) => ['panel-bg', 'dark-red'], }), new FieldColorSchemeMode({ id: 'continuous-greens', name: 'Greens', isContinuous: true, isByValue: true, - colors: ['panel-bg', 'dark-green'], + getColors: (theme: GrafanaTheme2) => ['panel-bg', 'dark-green'], }), new FieldColorSchemeMode({ id: 'continuous-purples', name: 'Purples', isContinuous: true, isByValue: true, - colors: ['panel-bg', 'dark-purple'], + getColors: (theme: GrafanaTheme2) => ['panel-bg', 'dark-purple'], }), ]; }); @@ -143,7 +125,7 @@ interface FieldColorSchemeModeOptions { id: string; name: string; description?: string; - colors: string[]; + getColors: (theme: GrafanaTheme2) => string[]; isContinuous: boolean; isByValue: boolean; } @@ -152,27 +134,34 @@ export class FieldColorSchemeMode implements FieldColorMode { id: string; name: string; description?: string; - colors: string[]; isContinuous: boolean; isByValue: boolean; colorCache?: string[]; + colorCacheTheme?: GrafanaTheme2; interpolator?: (value: number) => string; + getNamedColors?: (theme: GrafanaTheme2) => string[]; constructor(options: FieldColorSchemeModeOptions) { this.id = options.id; this.name = options.name; this.description = options.description; - this.colors = options.colors; + this.getNamedColors = options.getColors; this.isContinuous = options.isContinuous; this.isByValue = options.isByValue; } - private getColors(theme: GrafanaTheme2) { - if (this.colorCache) { + getColors(theme: GrafanaTheme2): string[] { + if (!this.getNamedColors) { + return []; + } + + if (this.colorCache && this.colorCacheTheme === theme) { return this.colorCache; } - this.colorCache = this.colors.map((c) => getColorForTheme(c, theme.v1)); + this.colorCache = this.getNamedColors(theme).map(theme.visualization.getColorByName); + this.colorCacheTheme = theme; + return this.colorCache; } @@ -243,6 +232,6 @@ export function getFieldSeriesColor(field: Field, theme: GrafanaTheme2): ColorSc function getFixedColor(field: Field, theme: GrafanaTheme2) { return () => { - return getColorForTheme(field.config.color?.fixedColor ?? FALLBACK_COLOR, theme.v1); + return theme.visualization.getColorByName(field.config.color?.fixedColor ?? FALLBACK_COLOR); }; } diff --git a/packages/grafana-data/src/field/scale.ts b/packages/grafana-data/src/field/scale.ts index 16394e0fcbf..c30fda912b5 100644 --- a/packages/grafana-data/src/field/scale.ts +++ b/packages/grafana-data/src/field/scale.ts @@ -2,7 +2,6 @@ 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'; @@ -42,21 +41,22 @@ export function getScaleCalculator(field: Field, theme: GrafanaTheme2): ScaleCal function getBooleanScaleCalculator(field: Field, theme: GrafanaTheme2): ScaleCalculator { const trueValue: ColorScaleValue = { - color: getColorForTheme('green', theme.v1), + color: theme.visualization.getColorByName('green'), percent: 1, threshold: (undefined as unknown) as Threshold, }; const falseValue: ColorScaleValue = { - color: getColorForTheme('red', theme.v1), + color: theme.visualization.getColorByName('red'), 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); + if (mode.isContinuous && mode.getColors) { + const colors = mode.getColors(theme); + trueValue.color = colors[colors.length - 1]; + falseValue.color = colors[0]; } return (value: number) => { diff --git a/packages/grafana-data/src/themes/createTheme.ts b/packages/grafana-data/src/themes/createTheme.ts index 194b8dec5c4..7b528b1bc9a 100644 --- a/packages/grafana-data/src/themes/createTheme.ts +++ b/packages/grafana-data/src/themes/createTheme.ts @@ -9,6 +9,7 @@ import { createTypography, ThemeTypographyInput } from './createTypography'; import { createV1Theme } from './createV1Theme'; import { GrafanaTheme2 } from './types'; import { zIndex } from './zIndex'; +import { createVisualizationColors } from './createVisualizationColors'; /** @internal */ export interface NewThemeOptions { @@ -37,6 +38,7 @@ export function createTheme(options: NewThemeOptions = {}): GrafanaTheme2 { const shadows = createShadows(colors); const transitions = createTransitions(); const components = createComponents(colors, shadows); + const visualization = createVisualizationColors(colors); const theme = { name, @@ -50,6 +52,7 @@ export function createTheme(options: NewThemeOptions = {}): GrafanaTheme2 { typography, shadows, transitions, + visualization, zIndex: { ...zIndex, }, diff --git a/packages/grafana-data/src/themes/createV1Theme.ts b/packages/grafana-data/src/themes/createV1Theme.ts index a1e46029c62..de9d3811257 100644 --- a/packages/grafana-data/src/themes/createV1Theme.ts +++ b/packages/grafana-data/src/themes/createV1Theme.ts @@ -229,6 +229,7 @@ export function createV1Theme(theme: Omit): GrafanaTheme { shadows: { listItem: 'none', }, + visualization: theme.visualization, }; } diff --git a/packages/grafana-data/src/themes/createVisualizationColors.test.ts b/packages/grafana-data/src/themes/createVisualizationColors.test.ts new file mode 100644 index 00000000000..129e4bf4352 --- /dev/null +++ b/packages/grafana-data/src/themes/createVisualizationColors.test.ts @@ -0,0 +1,32 @@ +import { createColors } from './createColors'; +import { createVisualizationColors } from './createVisualizationColors'; + +describe('createVizColors', () => { + const darkThemeColors = createColors({}); + const vizColors = createVisualizationColors(darkThemeColors); + + it('Can map named colors to real color', () => { + expect(vizColors.getColorByName('green')).toBe('#73BF69'); + }); + + it('Can map named colors using old aliases to real color', () => { + expect(vizColors.getColorByName('dark-green')).toBe('#37872D'); + }); + + it('Can get color from palette', () => { + expect(vizColors.palette[0]).not.toBeUndefined(); + }); + + it('returns color if specified as hex or rgb/a', () => { + expect(vizColors.getColorByName('#ff0000')).toBe('#ff0000'); + expect(vizColors.getColorByName('#ff0000')).toBe('#ff0000'); + expect(vizColors.getColorByName('#FF0000')).toBe('#FF0000'); + expect(vizColors.getColorByName('#CCC')).toBe('#CCC'); + expect(vizColors.getColorByName('rgb(0,0,0)')).toBe('rgb(0,0,0)'); + expect(vizColors.getColorByName('rgba(0,0,0,1)')).toBe('rgba(0,0,0,1)'); + }); + + it('returns hex for named color that is not a part of named colors palette', () => { + expect(vizColors.getColorByName('lime')).toBe('#00ff00'); + }); +}); diff --git a/packages/grafana-data/src/themes/createVisualizationColors.ts b/packages/grafana-data/src/themes/createVisualizationColors.ts new file mode 100644 index 00000000000..3a9a442180a --- /dev/null +++ b/packages/grafana-data/src/themes/createVisualizationColors.ts @@ -0,0 +1,521 @@ +import { FALLBACK_COLOR } from '../types'; +import { ThemeColors } from './createColors'; + +/** + * @alpha + */ +export interface ThemeVisualizationColors { + /** Only for internal use by color schemes */ + palette: string[]; + /** Lookup the real color given the name */ + getColorByName: (color: string) => string; + /** Colors organized by hue */ + hues: ThemeVizHue[]; +} + +/** + * @alpha + */ +export interface ThemeVizColor { + color: string; + name: string; + aliases?: string[]; + primary?: boolean; +} + +/** + * @alpha + */ +export interface ThemeVizHue { + name: string; + shades: ThemeVizColor[]; +} + +/** + * @internal + */ +export function createVisualizationColors(colors: ThemeColors): ThemeVisualizationColors { + let hues: ThemeVizHue[] = []; + + if (colors.mode === 'dark') { + hues = getDarkHues(); + } else if (colors.mode === 'light') { + hues = getLightHues(); + } + + const byNameIndex: Record = {}; + + for (const hue of hues) { + for (const shade of hue.shades) { + byNameIndex[shade.name] = shade.color; + if (shade.aliases) { + for (const alias of shade.aliases) { + byNameIndex[alias] = shade.color; + } + } + } + } + + // special colors + byNameIndex['transparent'] = 'rgba(0,0,0,0)'; + byNameIndex['panel-bg'] = colors.background.primary; + byNameIndex['text'] = colors.text.primary; + + const getColorByName = (colorName: string) => { + if (!colorName) { + return FALLBACK_COLOR; + } + + const realColor = byNameIndex[colorName]; + if (realColor) { + return realColor; + } + + if (colorName[0] === '#') { + return colorName; + } + + if (colorName.indexOf('rgb') > -1) { + return colorName; + } + + const nativeColor = nativeColorNames[colorName.toLowerCase()]; + if (nativeColor) { + byNameIndex[colorName] = nativeColor; + return nativeColor; + } + + return colorName; + }; + + const palette = getClassicPalette(); + + return { + hues, + palette, + getColorByName, + }; +} + +function getDarkHues(): ThemeVizHue[] { + return [ + { + name: 'red', + shades: [ + { color: '#FFA6B0', name: 'super-light-red' }, + { color: '#FF7383', name: 'light-red' }, + { color: '#F2495C', name: 'red', primary: true }, + { color: '#E02F44', name: 'semi-dark-red' }, + { color: '#C4162A', name: 'dark-red' }, + ], + }, + { + name: 'orange', + shades: [ + { color: '#FFCB7D', name: 'super-light-orange', aliases: [] }, + { color: '#FFB357', name: 'light-orange', aliases: [] }, + { color: '#FF9830', name: 'orange', aliases: [], primary: true }, + { color: '#FF780A', name: 'semi-dark-orange', aliases: [] }, + { color: '#FA6400', name: 'dark-orange', aliases: [] }, + ], + }, + { + name: 'yellow', + shades: [ + { color: '#FFF899', name: 'super-light-yellow', aliases: [] }, + { color: '#FFEE52', name: 'light-yellow', aliases: [] }, + { color: '#FADE2A', name: 'yellow', aliases: [], primary: true }, + { color: '#F2CC0C', name: 'semi-dark-yellow', aliases: [] }, + { color: '#E0B400', name: 'dark-yellow', aliases: [] }, + ], + }, + { + name: 'green', + shades: [ + { color: '#C8F2C2', name: 'super-light-green', aliases: [] }, + { color: '#96D98D', name: 'light-green', aliases: [] }, + { color: '#73BF69', name: 'green', aliases: [], primary: true }, + { color: '#56A64B', name: 'semi-dark-green', aliases: [] }, + { color: '#37872D', name: 'dark-green', aliases: [] }, + ], + }, + { + name: 'blue', + shades: [ + { color: '#C0D8FF', name: 'super-light-blue', aliases: [] }, + { color: '#8AB8FF', name: 'light-blue', aliases: [] }, + { color: '#5794F2', name: 'blue', aliases: [], primary: true }, + { color: '#3274D9', name: 'semi-dark-blue', aliases: [] }, + { color: '#1F60C4', name: 'dark-blue', aliases: [] }, + ], + }, + { + name: 'purple', + shades: [ + { color: '#DEB6F2', name: 'super-light-purple', aliases: [] }, + { color: '#CA95E5', name: 'light-purple', aliases: [] }, + { color: '#B877D9', name: 'purple', aliases: [], primary: true }, + { color: '#A352CC', name: 'semi-dark-purple', aliases: [] }, + { color: '#8F3BB8', name: 'dark-purple', aliases: [] }, + ], + }, + ]; +} + +function getLightHues(): ThemeVizHue[] { + return [ + { + name: 'red', + shades: [ + { color: '#FF7383', name: 'super-light-red' }, + { color: '#F2495C', name: 'light-red' }, + { color: '#E02F44', name: 'red', primary: true }, + { color: '#C4162A', name: 'semi-dark-red' }, + { color: '#AD0317', name: 'dark-red' }, + ], + }, + { + name: 'orange', + shades: [ + { color: '#FFB357', name: 'super-light-orange', aliases: [] }, + { color: '#FF9830', name: 'light-orange', aliases: [] }, + { color: '#FF780A', name: 'orange', aliases: [], primary: true }, + { color: '#FA6400', name: 'semi-dark-orange', aliases: [] }, + { color: '#E55400', name: 'dark-orange', aliases: [] }, + ], + }, + { + name: 'yellow', + shades: [ + { color: '#FFEE52', name: 'super-light-yellow', aliases: [] }, + { color: '#FADE2A', name: 'light-yellow', aliases: [] }, + { color: '#F2CC0C', name: 'yellow', aliases: [], primary: true }, + { color: '#E0B400', name: 'semi-dark-yellow', aliases: [] }, + { color: '#CC9D00', name: 'dark-yellow', aliases: [] }, + ], + }, + { + name: 'green', + shades: [ + { color: '#96D98D', name: 'super-light-green', aliases: [] }, + { color: '#73BF69', name: 'light-green', aliases: [] }, + { color: '#56A64B', name: 'green', aliases: [], primary: true }, + { color: '#37872D', name: 'semi-dark-green', aliases: [] }, + { color: '#19730E', name: 'dark-green', aliases: [] }, + ], + }, + { + name: 'blue', + shades: [ + { color: '#8AB8FF', name: 'super-light-blue', aliases: [] }, + { color: '#5794F2', name: 'light-blue', aliases: [] }, + { color: '#3274D9', name: 'blue', aliases: [], primary: true }, + { color: '#1F60C4', name: 'semi-dark-blue', aliases: [] }, + { color: '#1250B0', name: 'dark-blue', aliases: [] }, + ], + }, + { + name: 'purple', + shades: [ + { color: '#CA95E5', name: 'super-light-purple', aliases: [] }, + { color: '#B877D9', name: 'light-purple', aliases: [] }, + { color: '#A352CC', name: 'purple', aliases: [], primary: true }, + { color: '#8F3BB8', name: 'semi-dark-purple', aliases: [] }, + { color: '#7C2EA3', name: 'dark-purple', aliases: [] }, + ], + }, + ]; +} + +function getClassicPalette() { + // Todo replace these with named colors (as many as possible) + + return [ + 'green', // '#7EB26D', // 0: pale green + 'semi-dark-yellow', // '#EAB839', // 1: mustard + 'light-blue', // #6ED0E0', // 2: light blue + 'semi-dark-orange', // '#EF843C', // 3: orange + 'red', // '#E24D42', // 4: red + 'blue', // #1F78C1', // 5: ocean + 'purple', // '#BA43A9', // 6: purple + '#705DA0', // 7: violet + 'dark-green', // '#508642', // 8: dark green + 'yellow', //'#CCA300', // 9: dark sand + '#447EBC', + '#C15C17', + '#890F02', + '#0A437C', + '#6D1F62', + '#584477', + '#B7DBAB', + '#F4D598', + '#70DBED', + '#F9BA8F', + '#F29191', + '#82B5D8', + '#E5A8E2', + '#AEA2E0', + '#629E51', + '#E5AC0E', + '#64B0C8', + '#E0752D', + '#BF1B00', + '#0A50A1', + '#962D82', + '#614D93', + '#9AC48A', + '#F2C96D', + '#65C5DB', + '#F9934E', + '#EA6460', + '#5195CE', + '#D683CE', + '#806EB7', + '#3F6833', + '#967302', + '#2F575E', + '#99440A', + '#58140C', + '#052B51', + '#511749', + '#3F2B5B', + '#E0F9D7', + '#FCEACA', + '#CFFAFF', + '#F9E2D2', + '#FCE2DE', + '#BADFF4', + '#F9D9F9', + '#DEDAF7', + ]; +} + +// Old hues +// function getDarkHues(): ThemeVizHue[] { +// return [ +// { +// name: 'red', +// shades: [ +// { name: 'red1', color: '#FFC2D4', aliases: ['super-light-red'] }, +// { name: 'red2', color: '#FFA8C2', aliases: ['light-red'] }, +// { name: 'red3', color: '#FF85A9', aliases: ['red'], primary: true }, +// { name: 'red4', color: '#FF5286', aliases: ['semi-dark-red'] }, +// { name: 'red5', color: '#E0226E', aliases: ['dark-red'] }, +// ], +// }, +// { +// name: 'orange', +// shades: [ +// { name: 'orange1', color: '#FFC0AD', aliases: ['super-light-orange'] }, +// { name: 'orange2', color: '#FFA98F', aliases: ['light-orange'] }, +// { name: 'orange3', color: '#FF825C', aliases: ['orange'], primary: true }, +// { name: 'orange4', color: '#FF5F2E', aliases: ['semi-dark-orange'] }, +// { name: 'orange5', color: '#E73903', aliases: ['dark-orange'] }, +// ], +// }, +// { +// name: 'yellow', +// shades: [ +// { name: 'yellow1', color: '#FFE68F', aliases: ['super-light-yellow'] }, +// { name: 'yellow2', color: '#FAD34A', aliases: ['light-yellow'] }, +// { name: 'yellow3', color: '#ECBB09', aliases: ['yellow'], primary: true }, +// { name: 'yellow4', color: '#CFA302', aliases: ['semi-dark-yellow'] }, +// { name: 'yellow5', color: '#AD8800', aliases: ['dark-yellow'] }, +// ], +// }, +// { +// name: 'green', +// shades: [ +// { name: 'green1', color: '#93ECCB', aliases: ['super-light-green'] }, +// { name: 'green2', color: '#65DCB1', aliases: ['light-green'] }, +// { name: 'green3', color: '#2DC88F', aliases: ['green'], primary: true }, +// { name: 'green4', color: '#25A777', aliases: ['semi-dark-green'] }, +// { name: 'green5', color: '#1B855E', aliases: ['dark-green'] }, +// ], +// }, +// { +// name: 'teal', +// shades: [ +// { name: 'teal1', color: '#73E7F7' }, +// { name: 'teal2', color: '#2BD6EE' }, +// { name: 'teal3', color: '#11BDD4', primary: true }, +// { name: 'teal4', color: '#0EA0B4' }, +// { name: 'teal5', color: '#077D8D' }, +// ], +// }, +// { +// name: 'blue', +// shades: [ +// { name: 'blue1', color: '#C2D7FF', aliases: ['super-light-blue'] }, +// { name: 'blue2', color: '#A3C2FF', aliases: ['light-blue'] }, +// { name: 'blue3', color: '#83ACFC', aliases: ['blue'], primary: true }, +// { name: 'blue4', color: '#5D8FEF', aliases: ['semi-dark-blue'] }, +// { name: 'blue5', color: '#3871DC', aliases: ['dark-blue'] }, +// ], +// }, +// { +// name: 'violet', +// shades: [ +// { name: 'violet1', color: '#DACCFF' }, +// { name: 'violet2', color: '#C7B2FF' }, +// { name: 'violet3', color: '#B094FF', primary: true }, +// { name: 'violet4', color: '#9271EF' }, +// { name: 'violet5', color: '#7E63CA' }, +// ], +// }, +// { +// name: 'purple', +// shades: [ +// { name: 'purple1', color: '#FFBDFF', aliases: ['super-light-purple'] }, +// { name: 'purple2', color: '#F5A3F5', aliases: ['light-purple'] }, +// { name: 'purple3', color: '#E48BE4', aliases: ['purple'], primary: true }, +// { name: 'purple4', color: '#CA68CA', aliases: ['semi-dark-purple'] }, +// { name: 'purple5', color: '#B545B5', aliases: ['dark-purple'] }, +// ], +// }, +// ]; +// } + +const nativeColorNames: Record = { + aliceblue: '#f0f8ff', + antiquewhite: '#faebd7', + aqua: '#00ffff', + aquamarine: '#7fffd4', + azure: '#f0ffff', + beige: '#f5f5dc', + bisque: '#ffe4c4', + black: '#000000', + blanchedalmond: '#ffebcd', + blue: '#0000ff', + blueviolet: '#8a2be2', + brown: '#a52a2a', + burlywood: '#deb887', + cadetblue: '#5f9ea0', + chartreuse: '#7fff00', + chocolate: '#d2691e', + coral: '#ff7f50', + cornflowerblue: '#6495ed', + cornsilk: '#fff8dc', + crimson: '#dc143c', + cyan: '#00ffff', + darkblue: '#00008b', + darkcyan: '#008b8b', + darkgoldenrod: '#b8860b', + darkgray: '#a9a9a9', + darkgreen: '#006400', + darkkhaki: '#bdb76b', + darkmagenta: '#8b008b', + darkolivegreen: '#556b2f', + darkorange: '#ff8c00', + darkorchid: '#9932cc', + darkred: '#8b0000', + darksalmon: '#e9967a', + darkseagreen: '#8fbc8f', + darkslateblue: '#483d8b', + darkslategray: '#2f4f4f', + darkturquoise: '#00ced1', + darkviolet: '#9400d3', + deeppink: '#ff1493', + deepskyblue: '#00bfff', + dimgray: '#696969', + dodgerblue: '#1e90ff', + firebrick: '#b22222', + floralwhite: '#fffaf0', + forestgreen: '#228b22', + fuchsia: '#ff00ff', + gainsboro: '#dcdcdc', + ghostwhite: '#f8f8ff', + gold: '#ffd700', + goldenrod: '#daa520', + gray: '#808080', + green: '#008000', + greenyellow: '#adff2f', + honeydew: '#f0fff0', + hotpink: '#ff69b4', + 'indianred ': '#cd5c5c', + indigo: '#4b0082', + ivory: '#fffff0', + khaki: '#f0e68c', + lavender: '#e6e6fa', + lavenderblush: '#fff0f5', + lawngreen: '#7cfc00', + lemonchiffon: '#fffacd', + lightblue: '#add8e6', + lightcoral: '#f08080', + lightcyan: '#e0ffff', + lightgoldenrodyellow: '#fafad2', + lightgrey: '#d3d3d3', + lightgreen: '#90ee90', + lightpink: '#ffb6c1', + lightsalmon: '#ffa07a', + lightseagreen: '#20b2aa', + lightskyblue: '#87cefa', + lightslategray: '#778899', + lightsteelblue: '#b0c4de', + lightyellow: '#ffffe0', + lime: '#00ff00', + limegreen: '#32cd32', + linen: '#faf0e6', + magenta: '#ff00ff', + maroon: '#800000', + mediumaquamarine: '#66cdaa', + mediumblue: '#0000cd', + mediumorchid: '#ba55d3', + mediumpurple: '#9370d8', + mediumseagreen: '#3cb371', + mediumslateblue: '#7b68ee', + mediumspringgreen: '#00fa9a', + mediumturquoise: '#48d1cc', + mediumvioletred: '#c71585', + midnightblue: '#191970', + mintcream: '#f5fffa', + mistyrose: '#ffe4e1', + moccasin: '#ffe4b5', + navajowhite: '#ffdead', + navy: '#000080', + oldlace: '#fdf5e6', + olive: '#808000', + olivedrab: '#6b8e23', + orange: '#ffa500', + orangered: '#ff4500', + orchid: '#da70d6', + palegoldenrod: '#eee8aa', + palegreen: '#98fb98', + paleturquoise: '#afeeee', + palevioletred: '#d87093', + papayawhip: '#ffefd5', + peachpuff: '#ffdab9', + peru: '#cd853f', + pink: '#ffc0cb', + plum: '#dda0dd', + powderblue: '#b0e0e6', + purple: '#800080', + rebeccapurple: '#663399', + red: '#ff0000', + rosybrown: '#bc8f8f', + royalblue: '#4169e1', + saddlebrown: '#8b4513', + salmon: '#fa8072', + sandybrown: '#f4a460', + seagreen: '#2e8b57', + seashell: '#fff5ee', + sienna: '#a0522d', + silver: '#c0c0c0', + skyblue: '#87ceeb', + slateblue: '#6a5acd', + slategray: '#708090', + snow: '#fffafa', + springgreen: '#00ff7f', + steelblue: '#4682b4', + tan: '#d2b48c', + teal: '#008080', + thistle: '#d8bfd8', + tomato: '#ff6347', + turquoise: '#40e0d0', + violet: '#ee82ee', + wheat: '#f5deb3', + white: '#ffffff', + whitesmoke: '#f5f5f5', + yellow: '#ffff00', + yellowgreen: '#9acd32', +}; diff --git a/packages/grafana-data/src/themes/index.ts b/packages/grafana-data/src/themes/index.ts index b98120ce782..17cc7c10b77 100644 --- a/packages/grafana-data/src/themes/index.ts +++ b/packages/grafana-data/src/themes/index.ts @@ -8,7 +8,7 @@ export { ThemeTypography, ThemeTypographyVariant } from './createTypography'; export { ThemeTransitions } from './createTransitions'; export { ThemeSpacing } from './createSpacing'; export { ThemeZIndices } from './zIndex'; -export { palette } from './palette'; +export { ThemeVisualizationColors, ThemeVizColor, ThemeVizHue } from './createVisualizationColors'; /** Exporting the module like this to be able to generate docs properly. */ import * as colorManipulator from './colorManipulator'; diff --git a/packages/grafana-data/src/themes/types.ts b/packages/grafana-data/src/themes/types.ts index b62317ecfaa..d9b4595154c 100644 --- a/packages/grafana-data/src/themes/types.ts +++ b/packages/grafana-data/src/themes/types.ts @@ -8,6 +8,7 @@ import { ThemeSpacing } from './createSpacing'; import { ThemeTransitions } from './createTransitions'; import { ThemeTypography } from './createTypography'; import { ThemeZIndices } from './zIndex'; +import { ThemeVisualizationColors } from './createVisualizationColors'; /** * @beta @@ -25,6 +26,7 @@ export interface GrafanaTheme2 { typography: ThemeTypography; zIndex: ThemeZIndices; shadows: ThemeShadows; + visualization: ThemeVisualizationColors; transitions: ThemeTransitions; v1: GrafanaTheme; } diff --git a/packages/grafana-data/src/types/theme.ts b/packages/grafana-data/src/types/theme.ts index aee2b7fd5da..9fd7f9d046d 100644 --- a/packages/grafana-data/src/types/theme.ts +++ b/packages/grafana-data/src/types/theme.ts @@ -1,3 +1,5 @@ +import { ThemeVisualizationColors } from '../themes'; + export enum GrafanaThemeType { Light = 'light', Dark = 'dark', @@ -238,4 +240,5 @@ export interface GrafanaTheme extends GrafanaThemeCommons { shadows: { listItem: string; }; + visualization: ThemeVisualizationColors; } diff --git a/packages/grafana-data/src/utils/namedColorsPalette.test.ts b/packages/grafana-data/src/utils/namedColorsPalette.test.ts index 3cfc68c9e40..2ee9ab1437e 100644 --- a/packages/grafana-data/src/utils/namedColorsPalette.test.ts +++ b/packages/grafana-data/src/utils/namedColorsPalette.test.ts @@ -1,33 +1,16 @@ -import { getColorFromHexRgbOrName, getColorDefinitionByName } from './namedColorsPalette'; -import { GrafanaThemeType } from '../types/theme'; +import { getColorForTheme } from './namedColorsPalette'; +import { createTheme } from '../themes'; describe('colors', () => { - const SemiDarkBlue = getColorDefinitionByName('semi-dark-blue'); + const theme = createTheme(); describe('getColorFromHexRgbOrName', () => { it('returns black for unknown color', () => { - expect(getColorFromHexRgbOrName('aruba-sunshine')).toBe('#000000'); + expect(getColorForTheme('aruba-sunshine', theme.v1)).toBe('aruba-sunshine'); }); it('returns dark hex variant for known color if theme not specified', () => { - expect(getColorFromHexRgbOrName(SemiDarkBlue.name)).toBe(SemiDarkBlue.variants.dark); - }); - - it("returns correct variant's hex for known color if theme specified", () => { - expect(getColorFromHexRgbOrName(SemiDarkBlue.name, GrafanaThemeType.Light)).toBe(SemiDarkBlue.variants.light); - }); - - it('returns color if specified as hex or rgb/a', () => { - expect(getColorFromHexRgbOrName('ff0000')).toBe('#ff0000'); - expect(getColorFromHexRgbOrName('#ff0000')).toBe('#ff0000'); - expect(getColorFromHexRgbOrName('#FF0000')).toBe('#FF0000'); - expect(getColorFromHexRgbOrName('#CCC')).toBe('#CCC'); - expect(getColorFromHexRgbOrName('rgb(0,0,0)')).toBe('rgb(0,0,0)'); - expect(getColorFromHexRgbOrName('rgba(0,0,0,1)')).toBe('rgba(0,0,0,1)'); - }); - - it('returns hex for named color that is not a part of named colors palette', () => { - expect(getColorFromHexRgbOrName('lime')).toBe('#00ff00'); + expect(getColorForTheme('semi-dark-blue', theme.v1)).toBe('#3274D9'); }); }); }); diff --git a/packages/grafana-data/src/utils/namedColorsPalette.ts b/packages/grafana-data/src/utils/namedColorsPalette.ts index 8e48c5fa6ed..f3b51e8c9ec 100644 --- a/packages/grafana-data/src/utils/namedColorsPalette.ts +++ b/packages/grafana-data/src/utils/namedColorsPalette.ts @@ -1,216 +1,19 @@ -import { flatten } from 'lodash'; -import tinycolor from 'tinycolor2'; import { GrafanaTheme, GrafanaThemeType } from '../types/theme'; -type Hue = 'green' | 'yellow' | 'red' | 'blue' | 'orange' | 'purple'; - -export type Color = - | 'green' - | 'dark-green' - | 'semi-dark-green' - | 'light-green' - | 'super-light-green' - | 'yellow' - | 'dark-yellow' - | 'semi-dark-yellow' - | 'light-yellow' - | 'super-light-yellow' - | 'red' - | 'dark-red' - | 'semi-dark-red' - | 'light-red' - | 'super-light-red' - | 'blue' - | 'dark-blue' - | 'semi-dark-blue' - | 'light-blue' - | 'super-light-blue' - | 'orange' - | 'dark-orange' - | 'semi-dark-orange' - | 'light-orange' - | 'super-light-orange' - | 'purple' - | 'dark-purple' - | 'semi-dark-purple' - | 'light-purple' - | 'super-light-purple' - | 'panel-bg' - | 'transparent' - | 'text'; - -type ThemeVariants = { - dark: string; - light: string; -}; - -export type ColorDefinition = { - hue: Hue; - isPrimary?: boolean; - name: Color; - variants: ThemeVariants; -}; - -let colorsPaletteInstance: Map; -let colorsMap: Record | undefined; -let colorsMapTheme: GrafanaTheme | undefined; - -const buildColorDefinition = ( - hue: Hue, - name: Color, - [light, dark]: string[], - isPrimary?: boolean -): ColorDefinition => ({ - hue, - name, - variants: { - light, - dark, - }, - isPrimary: !!isPrimary, -}); - -export function getColorDefinitionByName(name: Color): ColorDefinition { - return flatten(Array.from(getNamedColorPalette().values())).filter((definition) => definition.name === name)[0]; -} - -export function buildColorsMapForTheme(theme: GrafanaTheme): Record { - theme = theme ?? GrafanaThemeType.Dark; - - colorsMap = {} as Record; - - for (const def of getNamedColorPalette().values()) { - for (const c of def) { - colorsMap[c.name] = c.variants[theme.type]; - } - } - - colorsMap['panel-bg'] = theme.colors.panelBg; - colorsMap['transparent'] = 'rgba(0,0,0,0)'; - colorsMap['text'] = theme.colors.text; - - return colorsMap; -} - +/** + * @deprecated use theme.vizColors.getByName + */ export function getColorForTheme(color: string, theme: GrafanaTheme): string { - if (!color) { - return 'gray'; - } - - // check if we need to rebuild cache - if (!colorsMap || colorsMapTheme !== theme) { - colorsMap = buildColorsMapForTheme(theme); - colorsMapTheme = theme; - } - - let realColor = colorsMap[color as Color]; - if (realColor) { - return realColor; - } - - if (color[0] === '#') { - return (colorsMap[color as Color] = color); - } - - if (color.indexOf('rgb') > -1) { - return (colorsMap[color as Color] = color); - } - - return (colorsMap[color as Color] = tinycolor(color).toHexString()); + return theme.visualization.getColorByName(color); } /** * @deprecated use getColorForTheme */ export function getColorFromHexRgbOrName(color: string, type?: GrafanaThemeType): string { - const themeType = type ?? GrafanaThemeType.Dark; - - if (themeType === GrafanaThemeType.Dark) { - const darkTheme = ({ - type: themeType, - colors: { - panelBg: '#141619', - }, - } as unknown) as GrafanaTheme; - - return getColorForTheme(color, darkTheme); - } - - const lightTheme = ({ - type: themeType, - colors: { - panelBg: '#000000', - }, - } as unknown) as GrafanaTheme; - - return getColorForTheme(color, lightTheme); + return 'gray'; } -const buildNamedColorsPalette = () => { - const palette = new Map(); - - const BasicGreen = buildColorDefinition('green', 'green', ['#56A64B', '#73BF69'], true); - const DarkGreen = buildColorDefinition('green', 'dark-green', ['#19730E', '#37872D']); - const SemiDarkGreen = buildColorDefinition('green', 'semi-dark-green', ['#37872D', '#56A64B']); - const LightGreen = buildColorDefinition('green', 'light-green', ['#73BF69', '#96D98D']); - const SuperLightGreen = buildColorDefinition('green', 'super-light-green', ['#96D98D', '#C8F2C2']); - - const BasicYellow = buildColorDefinition('yellow', 'yellow', ['#F2CC0C', '#FADE2A'], true); - const DarkYellow = buildColorDefinition('yellow', 'dark-yellow', ['#CC9D00', '#E0B400']); - const SemiDarkYellow = buildColorDefinition('yellow', 'semi-dark-yellow', ['#E0B400', '#F2CC0C']); - const LightYellow = buildColorDefinition('yellow', 'light-yellow', ['#FADE2A', '#FFEE52']); - const SuperLightYellow = buildColorDefinition('yellow', 'super-light-yellow', ['#FFEE52', '#FFF899']); - - const BasicRed = buildColorDefinition('red', 'red', ['#E02F44', '#F2495C'], true); - const DarkRed = buildColorDefinition('red', 'dark-red', ['#AD0317', '#C4162A']); - const SemiDarkRed = buildColorDefinition('red', 'semi-dark-red', ['#C4162A', '#E02F44']); - const LightRed = buildColorDefinition('red', 'light-red', ['#F2495C', '#FF7383']); - const SuperLightRed = buildColorDefinition('red', 'super-light-red', ['#FF7383', '#FFA6B0']); - - const BasicBlue = buildColorDefinition('blue', 'blue', ['#3274D9', '#5794F2'], true); - const DarkBlue = buildColorDefinition('blue', 'dark-blue', ['#1250B0', '#1F60C4']); - const SemiDarkBlue = buildColorDefinition('blue', 'semi-dark-blue', ['#1F60C4', '#3274D9']); - const LightBlue = buildColorDefinition('blue', 'light-blue', ['#5794F2', '#8AB8FF']); - const SuperLightBlue = buildColorDefinition('blue', 'super-light-blue', ['#8AB8FF', '#C0D8FF']); - - const BasicOrange = buildColorDefinition('orange', 'orange', ['#FF780A', '#FF9830'], true); - const DarkOrange = buildColorDefinition('orange', 'dark-orange', ['#E55400', '#FA6400']); - const SemiDarkOrange = buildColorDefinition('orange', 'semi-dark-orange', ['#FA6400', '#FF780A']); - const LightOrange = buildColorDefinition('orange', 'light-orange', ['#FF9830', '#FFB357']); - const SuperLightOrange = buildColorDefinition('orange', 'super-light-orange', ['#FFB357', '#FFCB7D']); - - const BasicPurple = buildColorDefinition('purple', 'purple', ['#A352CC', '#B877D9'], true); - const DarkPurple = buildColorDefinition('purple', 'dark-purple', ['#7C2EA3', '#8F3BB8']); - const SemiDarkPurple = buildColorDefinition('purple', 'semi-dark-purple', ['#8F3BB8', '#A352CC']); - const LightPurple = buildColorDefinition('purple', 'light-purple', ['#B877D9', '#CA95E5']); - const SuperLightPurple = buildColorDefinition('purple', 'super-light-purple', ['#CA95E5', '#DEB6F2']); - - const greens = [BasicGreen, DarkGreen, SemiDarkGreen, LightGreen, SuperLightGreen]; - const yellows = [BasicYellow, DarkYellow, SemiDarkYellow, LightYellow, SuperLightYellow]; - const reds = [BasicRed, DarkRed, SemiDarkRed, LightRed, SuperLightRed]; - const blues = [BasicBlue, DarkBlue, SemiDarkBlue, LightBlue, SuperLightBlue]; - const oranges = [BasicOrange, DarkOrange, SemiDarkOrange, LightOrange, SuperLightOrange]; - const purples = [BasicPurple, DarkPurple, SemiDarkPurple, LightPurple, SuperLightPurple]; - - palette.set('green', greens); - palette.set('yellow', yellows); - palette.set('red', reds); - palette.set('blue', blues); - palette.set('orange', oranges); - palette.set('purple', purples); - - return palette; -}; - -export const getNamedColorPalette = () => { - if (colorsPaletteInstance) { - return colorsPaletteInstance; - } - - colorsPaletteInstance = buildNamedColorsPalette(); - return colorsPaletteInstance; -}; - export const classicColors = [ '#7EB26D', // 0: pale green '#EAB839', // 1: mustard diff --git a/packages/grafana-ui/src/components/BarGauge/BarGauge.tsx b/packages/grafana-ui/src/components/BarGauge/BarGauge.tsx index b7b19c63630..c196fd13544 100644 --- a/packages/grafana-ui/src/components/BarGauge/BarGauge.tsx +++ b/packages/grafana-ui/src/components/BarGauge/BarGauge.tsx @@ -12,7 +12,6 @@ import { FieldConfig, FieldColorModeId, getFieldColorMode, - getColorForTheme, FALLBACK_COLOR, TextDisplayOptions, VizOrientation, @@ -536,7 +535,7 @@ export function getBarGradient(props: Props, maxSize: number): string { for (let i = 0; i < thresholds.steps.length; i++) { const threshold = thresholds.steps[i]; - const color = getColorForTheme(threshold.color, props.theme.v1); + const color = props.theme.visualization.getColorByName(threshold.color); const valuePercent = thresholds.mode === ThresholdsMode.Percentage ? threshold.value / 100 @@ -560,8 +559,9 @@ export function getBarGradient(props: Props, maxSize: number): string { return gradient + ')'; } - if (mode.isContinuous && mode.colors) { - const scheme = mode.colors.map((item) => getColorForTheme(item, theme.v1)); + if (mode.isContinuous && mode.getColors) { + const scheme = mode.getColors(theme); + for (let i = 0; i < scheme.length; i++) { const color = scheme[i]; diff --git a/packages/grafana-ui/src/components/BigValue/BigValue.test.tsx b/packages/grafana-ui/src/components/BigValue/BigValue.test.tsx index eb140b59632..94b995ccc99 100644 --- a/packages/grafana-ui/src/components/BigValue/BigValue.test.tsx +++ b/packages/grafana-ui/src/components/BigValue/BigValue.test.tsx @@ -12,6 +12,7 @@ function getProps(propOverrides?: Partial): Props { value: { text: '25', numeric: 25, + color: 'red', }, theme: createTheme(), }; diff --git a/packages/grafana-ui/src/components/BigValue/BigValueLayout.tsx b/packages/grafana-ui/src/components/BigValue/BigValueLayout.tsx index e62987dfeb6..0e9c3b40e7b 100644 --- a/packages/grafana-ui/src/components/BigValue/BigValueLayout.tsx +++ b/packages/grafana-ui/src/components/BigValue/BigValueLayout.tsx @@ -3,7 +3,7 @@ import React, { CSSProperties } from 'react'; import tinycolor from 'tinycolor2'; // Utils -import { formattedValueToString, DisplayValue, getColorForTheme, FieldConfig } from '@grafana/data'; +import { formattedValueToString, DisplayValue, FieldConfig } from '@grafana/data'; import { calculateFontSize } from '../../utils/measureText'; // Types @@ -30,9 +30,9 @@ export abstract class BigValueLayout { textValues: BigValueTextValues; constructor(private props: Props) { - const { width, height, value, theme, text } = props; + const { width, height, value, text } = props; - this.valueColor = getColorForTheme(value.color || 'green', theme.v1); + this.valueColor = value.color ?? 'gray'; this.panelPadding = height > 100 ? 12 : 8; this.textValues = getTextValues(props); this.justifyCenter = shouldJustifyCenter(props.justifyMode, this.textValues.title); diff --git a/packages/grafana-ui/src/components/BigValue/__snapshots__/BigValue.test.tsx.snap b/packages/grafana-ui/src/components/BigValue/__snapshots__/BigValue.test.tsx.snap index ee519a8f76b..5acf66fb54e 100644 --- a/packages/grafana-ui/src/components/BigValue/__snapshots__/BigValue.test.tsx.snap +++ b/packages/grafana-ui/src/components/BigValue/__snapshots__/BigValue.test.tsx.snap @@ -5,7 +5,7 @@ exports[`BigValue Render with basic options should render 1`] = ` style={ Object { "alignItems": "center", - "background": "linear-gradient(120deg, rgb(66, 154, 67), rgb(111, 183, 87))", + "background": "linear-gradient(120deg, rgb(179, 24, 0), rgb(230, 0, 31))", "borderRadius": "3px", "display": "flex", "flexDirection": "row", @@ -41,6 +41,7 @@ exports[`BigValue Render with basic options should render 1`] = ` } value={ Object { + "color": "red", "numeric": 25, "text": "25", "titleToAlignTo": undefined, diff --git a/packages/grafana-ui/src/components/ColorPicker/ColorPicker.tsx b/packages/grafana-ui/src/components/ColorPicker/ColorPicker.tsx index a523281f33b..e91750d10c9 100644 --- a/packages/grafana-ui/src/components/ColorPicker/ColorPicker.tsx +++ b/packages/grafana-ui/src/components/ColorPicker/ColorPicker.tsx @@ -2,7 +2,7 @@ import React, { Component, createRef } from 'react'; import { PopoverController } from '../Tooltip/PopoverController'; import { Popover } from '../Tooltip/Popover'; import { ColorPickerPopover, ColorPickerProps, ColorPickerChangeHandler } from './ColorPickerPopover'; -import { getColorForTheme, GrafanaTheme2 } from '@grafana/data'; +import { GrafanaTheme2 } from '@grafana/data'; import { SeriesColorPickerPopover } from './SeriesColorPickerPopover'; import { css } from '@emotion/css'; @@ -75,7 +75,7 @@ export const colorPickerFactory = ( ref={this.pickerTriggerRef} onClick={showPopper} onMouseLeave={hidePopper} - color={getColorForTheme(this.props.color || '#000000', theme.v1)} + color={theme.visualization.getColorByName(this.props.color || '#000000')} /> )} diff --git a/packages/grafana-ui/src/components/ColorPicker/ColorPickerPopover.test.tsx b/packages/grafana-ui/src/components/ColorPicker/ColorPickerPopover.test.tsx index eaf6969920c..1ce5b2f0700 100644 --- a/packages/grafana-ui/src/components/ColorPicker/ColorPickerPopover.test.tsx +++ b/packages/grafana-ui/src/components/ColorPicker/ColorPickerPopover.test.tsx @@ -1,13 +1,12 @@ import React from 'react'; import { mount, ReactWrapper } from 'enzyme'; import { ColorPickerPopover } from './ColorPickerPopover'; -import { flatten } from 'lodash'; -import { getNamedColorPalette, getColorFromHexRgbOrName } from '@grafana/data'; import { ColorSwatch } from './ColorSwatch'; - -const allColors = flatten(Array.from(getNamedColorPalette().values())); +import { createTheme, getColorForTheme } from '@grafana/data'; describe('ColorPickerPopover', () => { + const theme = createTheme(); + describe('rendering', () => { it('should render provided color as selected if color provided by name', () => { const wrapper = mount( {}} />); @@ -15,18 +14,7 @@ describe('ColorPickerPopover', () => { const notSelectedSwatches = wrapper.find(ColorSwatch).filterWhere((node) => node.prop('isSelected') === false); expect(selectedSwatch.length).toBe(1); - expect(notSelectedSwatches.length).toBe(allColors.length + 1); - expect(selectedSwatch.prop('isSelected')).toBe(true); - }); - - it('should render provided color as selected if color provided by hex', () => { - const wrapper = mount( {}} />); - - const selectedSwatch = wrapper.find(ColorSwatch).findWhere((node) => node.key() === 'green'); - const notSelectedSwatches = wrapper.find(ColorSwatch).filterWhere((node) => node.prop('isSelected') === false); - - expect(selectedSwatch.length).toBe(1); - expect(notSelectedSwatches.length).toBe(allColors.length + 1); + expect(notSelectedSwatches.length).toBe(31); expect(selectedSwatch.prop('isSelected')).toBe(true); }); }); @@ -47,7 +35,7 @@ describe('ColorPickerPopover', () => { basicBlueSwatch.simulate('click'); expect(onChangeSpy).toBeCalledTimes(1); - expect(onChangeSpy).toBeCalledWith(getColorFromHexRgbOrName('green')); + expect(onChangeSpy).toBeCalledWith(getColorForTheme('green', theme.v1)); }); it('should pass color name to onChange prop when named colors enabled', () => { diff --git a/packages/grafana-ui/src/components/ColorPicker/ColorPickerPopover.tsx b/packages/grafana-ui/src/components/ColorPicker/ColorPickerPopover.tsx index 8103c1ec2ad..4e1abf412ce 100644 --- a/packages/grafana-ui/src/components/ColorPicker/ColorPickerPopover.tsx +++ b/packages/grafana-ui/src/components/ColorPicker/ColorPickerPopover.tsx @@ -5,7 +5,7 @@ import SpectrumPalette from './SpectrumPalette'; import { Themeable2 } from '../../types/theme'; import { warnAboutColorPickerPropsDeprecation } from './warnAboutColorPickerPropsDeprecation'; import { css } from '@emotion/css'; -import { GrafanaTheme2, getColorForTheme } from '@grafana/data'; +import { GrafanaTheme2 } from '@grafana/data'; import { stylesFactory, withTheme2 } from '../../themes'; export type ColorPickerChangeHandler = (color: string) => void; @@ -59,7 +59,7 @@ class UnThemedColorPickerPopover extends Reac if (enableNamedColors) { return changeHandler(color); } - changeHandler(getColorForTheme(color, theme.v1)); + changeHandler(theme.visualization.getColorByName(color)); }; onTabChange = (tab: PickerType | keyof T) => { diff --git a/packages/grafana-ui/src/components/ColorPicker/NamedColorsGroup.tsx b/packages/grafana-ui/src/components/ColorPicker/NamedColorsGroup.tsx index aeeffd8023d..0e1d77b5c4f 100644 --- a/packages/grafana-ui/src/components/ColorPicker/NamedColorsGroup.tsx +++ b/packages/grafana-ui/src/components/ColorPicker/NamedColorsGroup.tsx @@ -1,38 +1,34 @@ import React, { FunctionComponent } from 'react'; -import { ColorDefinition } from '@grafana/data'; +import { ThemeVizHue } from '@grafana/data'; import { Color } from 'csstype'; -import { upperFirst, find } from 'lodash'; import { ColorSwatch, ColorSwatchVariant } from './ColorSwatch'; -import { useTheme2 } from '../../themes/ThemeContext'; - -type ColorChangeHandler = (color: ColorDefinition) => void; +import { upperFirst } from 'lodash'; interface NamedColorsGroupProps { - colors: ColorDefinition[]; + hue: ThemeVizHue; selectedColor?: Color; - onColorSelect: ColorChangeHandler; + onColorSelect: (colorName: string) => void; key?: string; } const NamedColorsGroup: FunctionComponent = ({ - colors, + hue, selectedColor, onColorSelect, ...otherProps }) => { - const theme = useTheme2(); - const primaryColor = find(colors, (color) => !!color.isPrimary); + const primaryShade = hue.shades.find((shade) => shade.primary)!; return (
- {primaryColor && ( + {primaryShade && ( onColorSelect(primaryColor)} + color={primaryShade.color} + label={upperFirst(hue.name)} + onClick={() => onColorSelect(primaryShade.name)} /> )}
= ({ marginTop: '8px', }} > - {colors.map( - (color) => - !color.isPrimary && ( -
+ {hue.shades.map( + (shade) => + !shade.primary && ( +
onColorSelect(color)} + key={shade.name} + isSelected={shade.name === selectedColor} + color={shade.color} + onClick={() => onColorSelect(shade.name)} />
) diff --git a/packages/grafana-ui/src/components/ColorPicker/NamedColorsPalette.test.tsx b/packages/grafana-ui/src/components/ColorPicker/NamedColorsPalette.test.tsx index 00ee361469d..023dad1f14a 100644 --- a/packages/grafana-ui/src/components/ColorPicker/NamedColorsPalette.test.tsx +++ b/packages/grafana-ui/src/components/ColorPicker/NamedColorsPalette.test.tsx @@ -1,12 +1,13 @@ import React from 'react'; import { mount, ReactWrapper } from 'enzyme'; import { NamedColorsPalette } from './NamedColorsPalette'; -import { createTheme, getColorDefinitionByName } from '@grafana/data'; +import { createTheme } from '@grafana/data'; import { ColorSwatch } from './ColorSwatch'; -import { ThemeContext } from '../../themes'; describe('NamedColorsPalette', () => { - const BasicGreen = getColorDefinitionByName('green'); + const theme = createTheme(); + const greenHue = theme.visualization.hues.find((x) => x.name === 'green')!; + const selectedShade = greenHue.shades[2]; describe('theme support for named colors', () => { let wrapper: ReactWrapper, selectedSwatch; @@ -16,21 +17,9 @@ describe('NamedColorsPalette', () => { }); it('should render provided color variant specific for theme', () => { - wrapper = mount( {}} />); - selectedSwatch = wrapper.find(ColorSwatch).findWhere((node) => node.key() === BasicGreen.name); - expect(selectedSwatch.prop('color')).toBe(BasicGreen.variants.dark); - - wrapper.unmount(); - - const withLightTheme = ( - - {}} /> - - ); - - wrapper = mount(withLightTheme); - selectedSwatch = wrapper.find(ColorSwatch).findWhere((node) => node.key() === BasicGreen.name); - expect(selectedSwatch.prop('color')).toBe(BasicGreen.variants.light); + wrapper = mount( {}} />); + selectedSwatch = wrapper.find(ColorSwatch).findWhere((node) => node.key() === selectedShade.name); + expect(selectedSwatch.prop('color')).toBe(selectedShade.color); }); }); }); diff --git a/packages/grafana-ui/src/components/ColorPicker/NamedColorsPalette.tsx b/packages/grafana-ui/src/components/ColorPicker/NamedColorsPalette.tsx index a57cc3b0bfe..d05959fcddf 100644 --- a/packages/grafana-ui/src/components/ColorPicker/NamedColorsPalette.tsx +++ b/packages/grafana-ui/src/components/ColorPicker/NamedColorsPalette.tsx @@ -1,5 +1,4 @@ import React from 'react'; -import { getNamedColorPalette } from '@grafana/data'; import NamedColorsGroup from './NamedColorsGroup'; import { VerticalGroup } from '../Layout/Layout'; import { ColorSwatch } from './ColorSwatch'; @@ -14,18 +13,9 @@ export const NamedColorsPalette = ({ color, onChange }: NamedColorsPaletteProps) const theme = useTheme2(); const swatches: JSX.Element[] = []; - getNamedColorPalette().forEach((colors, hue) => { - swatches.push( - { - onChange(color.name); - }} - /> - ); - }); + for (const hue of theme.visualization.hues) { + swatches.push(); + } return ( @@ -39,6 +29,7 @@ export const NamedColorsPalette = ({ color, onChange }: NamedColorsPaletteProps) }} > {swatches} +
{ - const theme = useTheme(); - const styles = useStyles(getStyles); + const theme = useTheme2(); + const styles = useStyles2(getStyles); const colorMode = getFieldColorMode(value?.mode); const availableOptions = item.settings?.byValueSupport @@ -90,7 +89,7 @@ export const FieldColorEditor: React.FC -
+