From 566cd2c6af8dc95b00b79d30e124b9517739f446 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Mon, 19 Oct 2020 10:25:27 +0200 Subject: [PATCH] ColorSchemes: Adds more color schemes and text colors that depend on the background (#28305) * Adding more color modes and text colors that depend on the background color * Updates * Updated * Another big value fix * Fixing unit tests * Updated * Updated test * Update * Updated * Updated * Updated * Updated * Added new demo dashboard * Updated * updated * Updated * Updateed * added beta notice * Fixed e2e test --- .../panel-common/color_modes.json | 338 ++++++++++++++++++ ...emplating-dashboard-links-and-variables.ts | 2 +- .../src/field/displayProcessor.ts | 5 +- .../grafana-data/src/field/fieldColor.test.ts | 8 +- packages/grafana-data/src/field/fieldColor.ts | 69 +++- .../src/field/fieldDisplay.test.ts | 4 +- .../src/field/fieldOverrides.test.ts | 16 +- .../field/getFieldDisplayValuesProxy.test.tsx | 3 +- packages/grafana-data/src/field/scale.test.ts | 5 +- .../src/utils/namedColorsPalette.ts | 25 +- .../src/utils/testdata/testTheme.ts | 12 + .../components/BigValue/BigValueLayout.tsx | 5 +- .../__snapshots__/BigValue.test.tsx.snap | 2 +- .../src/components/OptionsUI/fieldColor.tsx | 4 +- .../src/components/Table/DefaultCell.tsx | 8 +- packages/grafana-ui/src/utils/colors.ts | 7 + packages/grafana-ui/src/utils/measureText.ts | 6 + .../panel/panellinks/linkSuppliers.test.ts | 5 +- .../panel/table-old/specs/renderer.test.ts | 8 +- 19 files changed, 494 insertions(+), 38 deletions(-) create mode 100644 devenv/dev-dashboards/panel-common/color_modes.json create mode 100644 packages/grafana-data/src/utils/testdata/testTheme.ts diff --git a/devenv/dev-dashboards/panel-common/color_modes.json b/devenv/dev-dashboards/panel-common/color_modes.json new file mode 100644 index 00000000000..5802e623380 --- /dev/null +++ b/devenv/dev-dashboards/panel-common/color_modes.json @@ -0,0 +1,338 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": "-- Grafana --", + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "editable": true, + "gnetId": null, + "graphTooltip": 0, + "links": [], + "panels": [ + { + "datasource": "gdev-testdata", + "fieldConfig": { + "defaults": { + "color": { + "mode": "continuous-BlYlRd" + }, + "custom": { + "align": "center", + "displayMode": "color-background", + "filterable": false + }, + "mappings": [], + "thresholds": { + "mode": "percentage", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "blue", + "value": 20 + }, + { + "color": "orange", + "value": 60 + }, + { + "color": "red", + "value": 70 + } + ] + }, + "unit": "degree" + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "Field" + }, + "properties": [ + { + "id": "custom.displayMode" + } + ] + } + ] + }, + "gridPos": { + "h": 16, + "w": 19, + "x": 0, + "y": 0 + }, + "id": 4, + "options": { + "showHeader": true, + "sortBy": [ + { + "desc": true, + "displayName": "Last" + } + ] + }, + "pluginVersion": "7.4.0-pre", + "targets": [ + { + "alias": "", + "csvWave": { + "timeStep": 60, + "valuesCSV": "0,0,2,2,1,1" + }, + "lines": 10, + "points": [], + "pulseWave": { + "offCount": 3, + "offValue": 1, + "onCount": 3, + "onValue": 2, + "timeStep": 60 + }, + "refId": "A", + "scenarioId": "random_walk", + "seriesCount": 15, + "stream": { + "bands": 1, + "noise": 2.2, + "speed": 250, + "spread": 3.5, + "type": "signal" + }, + "stringInput": "" + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Gradient color schemes", + "transformations": [ + { + "id": "reduce", + "options": { + "reducers": ["max", "mean", "last", "min"] + } + }, + { + "id": "organize", + "options": { + "excludeByName": { + "Field": false + }, + "indexByName": {}, + "renameByName": {} + } + } + ], + "type": "table" + }, + { + "datasource": null, + "fieldConfig": { + "defaults": { + "color": { + "mode": "continuous-blues" + }, + "custom": {}, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 20 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 26, + "w": 5, + "x": 19, + "y": 0 + }, + "id": 2, + "options": { + "colorMode": "background", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": ["mean"], + "fields": "", + "values": false + }, + "textMode": "value" + }, + "pluginVersion": "7.4.0-pre", + "targets": [ + { + "alias": "", + "csvWave": { + "timeStep": 60, + "valuesCSV": "0,0,2,2,1,1" + }, + "labels": "", + "lines": 10, + "points": [], + "pulseWave": { + "offCount": 3, + "offValue": 1, + "onCount": 3, + "onValue": 2, + "timeStep": 60 + }, + "refId": "A", + "scenarioId": "random_walk", + "seriesCount": 30, + "stream": { + "bands": 1, + "noise": 2.2, + "speed": 250, + "spread": 3.5, + "type": "signal" + }, + "stringInput": "" + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Stats", + "type": "stat" + }, + { + "datasource": "gdev-testdata", + "fieldConfig": { + "defaults": { + "color": { + "mode": "continuous-GrYlRd" + }, + "custom": { + "align": "center", + "displayMode": "color-background", + "filterable": false + }, + "mappings": [], + "thresholds": { + "mode": "percentage", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "blue", + "value": 20 + }, + { + "color": "orange", + "value": 60 + }, + { + "color": "red", + "value": 70 + } + ] + }, + "unit": "degree" + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "Field" + }, + "properties": [ + { + "id": "custom.displayMode" + } + ] + } + ] + }, + "gridPos": { + "h": 10, + "w": 19, + "x": 0, + "y": 16 + }, + "id": 5, + "options": { + "displayMode": "lcd", + "orientation": "auto", + "reduceOptions": { + "calcs": ["mean"], + "fields": "", + "values": false + }, + "showUnfilled": true + }, + "pluginVersion": "7.4.0-pre", + "targets": [ + { + "alias": "", + "csvWave": { + "timeStep": 60, + "valuesCSV": "0,0,2,2,1,1" + }, + "lines": 10, + "points": [], + "pulseWave": { + "offCount": 3, + "offValue": 1, + "onCount": 3, + "onValue": 2, + "timeStep": 60 + }, + "refId": "A", + "scenarioId": "random_walk", + "seriesCount": 15, + "stream": { + "bands": 1, + "noise": 2.2, + "speed": 250, + "spread": 3.5, + "type": "signal" + }, + "stringInput": "" + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Bar Gauge LCD", + "transformations": [], + "type": "bargauge" + } + ], + "schemaVersion": 26, + "style": "dark", + "tags": ["gdev", "demo"], + "templating": { + "list": [] + }, + "time": { + "from": "now-6h", + "to": "now" + }, + "timepicker": {}, + "timezone": "", + "title": "Gradient Color modes", + "uid": "inxsweKGz", + "version": 17 +} diff --git a/e2e/suite1/specs/templating-dashboard-links-and-variables.ts b/e2e/suite1/specs/templating-dashboard-links-and-variables.ts index 336fe11e69e..963510d6a9a 100644 --- a/e2e/suite1/specs/templating-dashboard-links-and-variables.ts +++ b/e2e/suite1/specs/templating-dashboard-links-and-variables.ts @@ -29,7 +29,7 @@ e2e.scenario({ e2e.components.DashboardLinks.link() .should('be.visible') .and(links => { - expect(links).to.have.length(13); + expect(links).to.have.length.greaterThan(13); for (let index = 0; index < links.length; index++) { expect(Cypress.$(links[index]).attr('href')).contains(`var-custom=${variableValue}`); diff --git a/packages/grafana-data/src/field/displayProcessor.ts b/packages/grafana-data/src/field/displayProcessor.ts index 180a25e6952..66705096ebf 100644 --- a/packages/grafana-data/src/field/displayProcessor.ts +++ b/packages/grafana-data/src/field/displayProcessor.ts @@ -3,13 +3,14 @@ import _ from 'lodash'; // Types import { Field, FieldType } from '../types/dataFrame'; -import { GrafanaTheme, GrafanaThemeType } from '../types/theme'; +import { GrafanaTheme } from '../types/theme'; import { DecimalCount, DecimalInfo, DisplayProcessor, DisplayValue } from '../types/displayValue'; import { getValueFormat } from '../valueFormats/valueFormats'; import { getMappedValue } from '../utils/valueMappings'; import { dateTime } from '../datetime'; import { KeyValue, TimeZone } from '../types'; import { getScaleCalculator } from './scale'; +import { getTestTheme } from '../utils/testdata/testTheme'; interface DisplayProcessorOptions { field: Partial; @@ -41,7 +42,7 @@ export function getDisplayProcessor(options?: DisplayProcessorOptions): DisplayP const config = field.config ?? {}; // Theme should be required or we need access to default theme instance from here - const theme = options.theme ?? ({ type: GrafanaThemeType.Dark } as GrafanaTheme); + const theme = options.theme ?? getTestTheme(); let unit = config.unit; let hasDateUnit = unit && (timeFormats[unit] || unit.startsWith('time:')); diff --git a/packages/grafana-data/src/field/fieldColor.test.ts b/packages/grafana-data/src/field/fieldColor.test.ts index 638f4beb9b8..6949a25284b 100644 --- a/packages/grafana-data/src/field/fieldColor.test.ts +++ b/packages/grafana-data/src/field/fieldColor.test.ts @@ -1,4 +1,5 @@ -import { Field, GrafanaThemeType, GrafanaTheme, FieldColorModeId } from '../types'; +import { Field, FieldColorModeId } from '../types'; +import { getTestTheme } from '../utils/testdata/testTheme'; import { fieldColorModeRegistry, FieldValueColorCalculator } from './fieldColor'; describe('fieldColorModeRegistry', () => { @@ -9,10 +10,7 @@ describe('fieldColorModeRegistry', () => { function getCalculator(options: GetCalcOptions): FieldValueColorCalculator { const mode = fieldColorModeRegistry.get(options.mode); - return mode.getCalculator( - { state: { seriesIndex: options.seriesIndex } } as Field, - { type: GrafanaThemeType.Dark } as GrafanaTheme - ); + return mode.getCalculator({ state: { seriesIndex: options.seriesIndex } } as Field, getTestTheme()); } it('Schemes should interpolate', () => { diff --git a/packages/grafana-data/src/field/fieldColor.ts b/packages/grafana-data/src/field/fieldColor.ts index efadedd148c..869473655ad 100644 --- a/packages/grafana-data/src/field/fieldColor.ts +++ b/packages/grafana-data/src/field/fieldColor.ts @@ -54,20 +54,81 @@ export const fieldColorModeRegistry = new Registry(() => { // }), new FieldColorSchemeMode({ id: FieldColorModeId.PaletteClassic, - name: 'By series / Classic palette', - //description: 'Assigns color based on series or field index', + name: 'Classic palette', isContinuous: false, isByValue: false, colors: classicColors, }), new FieldColorSchemeMode({ id: 'continuous-GrYlRd', - name: 'By value / Green Yellow Red (gradient)', - //description: 'Interpolated colors based value, min and max', + name: 'Green-Yellow-Red', isContinuous: true, isByValue: true, colors: ['green', 'yellow', 'red'], }), + new FieldColorSchemeMode({ + id: 'continuous-BlYlRd', + name: 'Blue-Yellow-Red', + isContinuous: true, + isByValue: true, + colors: ['dark-blue', 'super-light-yellow', 'dark-red'], + }), + new FieldColorSchemeMode({ + id: 'continuous-RdYlBl', + name: 'Red-Yellow-Blue', + isContinuous: true, + isByValue: true, + colors: ['dark-red', 'super-light-yellow', 'dark-blue'], + }), + new FieldColorSchemeMode({ + id: 'continuous-YlRd', + name: 'Yellow-Red', + isContinuous: true, + isByValue: true, + colors: ['super-light-yellow', 'dark-red'], + }), + new FieldColorSchemeMode({ + id: 'continuous-BlPu', + name: 'Blue-Purple', + isContinuous: true, + isByValue: true, + colors: ['blue', 'purple'], + }), + new FieldColorSchemeMode({ + id: 'continuous-YlBl', + name: 'Yellow-Blue', + isContinuous: true, + isByValue: true, + colors: ['super-light-yellow', 'dark-blue'], + }), + new FieldColorSchemeMode({ + id: 'continuous-blues', + name: 'Blues', + isContinuous: true, + isByValue: true, + colors: ['panel-bg', 'dark-blue'], + }), + new FieldColorSchemeMode({ + id: 'continuous-reds', + name: 'Reds', + isContinuous: true, + isByValue: true, + colors: ['panel-bg', 'dark-red'], + }), + new FieldColorSchemeMode({ + id: 'continuous-greens', + name: 'Greens', + isContinuous: true, + isByValue: true, + colors: ['panel-bg', 'dark-green'], + }), + new FieldColorSchemeMode({ + id: 'continuous-purples', + name: 'Purples', + isContinuous: true, + isByValue: true, + colors: ['panel-bg', 'dark-purple'], + }), ]; }); diff --git a/packages/grafana-data/src/field/fieldDisplay.test.ts b/packages/grafana-data/src/field/fieldDisplay.test.ts index 67651bf1764..4998efcb31b 100644 --- a/packages/grafana-data/src/field/fieldDisplay.test.ts +++ b/packages/grafana-data/src/field/fieldDisplay.test.ts @@ -2,9 +2,9 @@ import merge from 'lodash/merge'; import { getFieldDisplayValues, GetFieldDisplayValuesOptions } from './fieldDisplay'; import { toDataFrame } from '../dataframe/processDataFrame'; import { ReducerID } from '../transformations/fieldReducer'; -import { GrafanaTheme } from '../types/theme'; import { MappingType } from '../types'; import { standardFieldConfigEditorRegistry } from './standardFieldConfigEditorRegistry'; +import { getTestTheme } from '../utils/testdata/testTheme'; describe('FieldDisplay', () => { beforeAll(() => { @@ -241,7 +241,7 @@ function createDisplayOptions(extend: Partial = {} overrides: [], defaults: {}, }, - theme: {} as GrafanaTheme, + theme: getTestTheme(), }; return merge(options, extend); diff --git a/packages/grafana-data/src/field/fieldOverrides.test.ts b/packages/grafana-data/src/field/fieldOverrides.test.ts index 86dca6f647f..f7e26bfadbc 100644 --- a/packages/grafana-data/src/field/fieldOverrides.test.ts +++ b/packages/grafana-data/src/field/fieldOverrides.test.ts @@ -15,7 +15,6 @@ import { FieldConfigPropertyItem, FieldConfigSource, FieldType, - GrafanaTheme, InterpolateFunction, ThresholdsMode, FieldColorModeId, @@ -28,6 +27,7 @@ import { FieldConfigOptionsRegistry } from './FieldConfigOptionsRegistry'; import { getFieldDisplayName } from './fieldState'; import { ArrayVector } from '../vector'; import { getDisplayProcessor } from './displayProcessor'; +import { getTestTheme } from '../utils/testdata/testTheme'; const property1: any = { id: 'custom.property1', // Match field properties @@ -136,7 +136,7 @@ describe('applyFieldOverrides', () => { }, replaceVariables: (value: any) => value, getDataSourceSettingsByUid: undefined as any, - theme: {} as GrafanaTheme, + theme: getTestTheme(), fieldConfigRegistry: new FieldConfigOptionsRegistry(), }); @@ -199,7 +199,7 @@ describe('applyFieldOverrides', () => { fieldConfigRegistry: customFieldRegistry, getDataSourceSettingsByUid: undefined as any, replaceVariables: v => v, - theme: {} as GrafanaTheme, + theme: getTestTheme(), })[0]; const outField = processed.fields[0]; @@ -216,7 +216,7 @@ describe('applyFieldOverrides', () => { fieldConfig: src as FieldConfigSource, // defaults + overrides replaceVariables: (undefined as any) as InterpolateFunction, getDataSourceSettingsByUid: undefined as any, - theme: (undefined as any) as GrafanaTheme, + theme: getTestTheme(), fieldConfigRegistry: customFieldRegistry, })[0]; const valueColumn = data.fields[1]; @@ -244,7 +244,7 @@ describe('applyFieldOverrides', () => { fieldConfig: src as FieldConfigSource, // defaults + overrides replaceVariables: (undefined as any) as InterpolateFunction, getDataSourceSettingsByUid: undefined as any, - theme: (undefined as any) as GrafanaTheme, + theme: getTestTheme(), autoMinMax: true, })[0]; const valueColumn = data.fields[1]; @@ -268,7 +268,7 @@ describe('applyFieldOverrides', () => { return value; }) as InterpolateFunction, getDataSourceSettingsByUid: undefined as any, - theme: (undefined as any) as GrafanaTheme, + theme: getTestTheme(), autoMinMax: true, fieldConfigRegistry: customFieldRegistry, })[0]; @@ -521,7 +521,7 @@ describe('getLinksSupplier', () => { // this is used only for internal links so isn't needed here () => ({} as any), { - theme: {} as GrafanaTheme, + theme: getTestTheme(), } ); supplier({}); @@ -568,7 +568,7 @@ describe('getLinksSupplier', () => { // We do not need to interpolate anything for this test (value, vars, format) => value, uid => ({ name: 'testDS' } as any), - { theme: {} as GrafanaTheme } + { theme: getTestTheme() } ); const links = supplier({ valueRowIndex: 0 }); expect(links.length).toBe(1); diff --git a/packages/grafana-data/src/field/getFieldDisplayValuesProxy.test.tsx b/packages/grafana-data/src/field/getFieldDisplayValuesProxy.test.tsx index f4e746eee82..e1dc2a5aad9 100644 --- a/packages/grafana-data/src/field/getFieldDisplayValuesProxy.test.tsx +++ b/packages/grafana-data/src/field/getFieldDisplayValuesProxy.test.tsx @@ -2,6 +2,7 @@ import { getFieldDisplayValuesProxy } from './getFieldDisplayValuesProxy'; import { applyFieldOverrides } from './fieldOverrides'; import { toDataFrame } from '../dataframe'; import { GrafanaTheme } from '../types'; +import { getTestTheme } from '../utils/testdata/testTheme'; describe('getFieldDisplayValuesProxy', () => { const data = applyFieldOverrides({ @@ -30,7 +31,7 @@ describe('getFieldDisplayValuesProxy', () => { replaceVariables: (val: string) => val, getDataSourceSettingsByUid: (val: string) => ({} as any), timeZone: 'utc', - theme: {} as GrafanaTheme, + theme: getTestTheme(), autoMinMax: true, })[0]; diff --git a/packages/grafana-data/src/field/scale.test.ts b/packages/grafana-data/src/field/scale.test.ts index a11c2497a9f..0d284da4648 100644 --- a/packages/grafana-data/src/field/scale.test.ts +++ b/packages/grafana-data/src/field/scale.test.ts @@ -1,7 +1,8 @@ -import { ThresholdsMode, Field, FieldType, GrafanaThemeType, GrafanaTheme } from '../types'; +import { ThresholdsMode, Field, FieldType } from '../types'; import { sortThresholds } from './thresholds'; import { ArrayVector } from '../vector/ArrayVector'; import { getScaleCalculator } from './scale'; +import { getTestTheme } from '../utils/testdata/testTheme'; describe('getScaleCalculator', () => { it('should return percent, threshold and color', () => { @@ -18,7 +19,7 @@ describe('getScaleCalculator', () => { values: new ArrayVector([0, 50, 100]), }; - const calc = getScaleCalculator(field, { type: GrafanaThemeType.Dark } as GrafanaTheme); + const calc = getScaleCalculator(field, getTestTheme()); expect(calc(70)).toEqual({ percent: 0.7, threshold: thresholds[1], diff --git a/packages/grafana-data/src/utils/namedColorsPalette.ts b/packages/grafana-data/src/utils/namedColorsPalette.ts index 19795150259..e18e6feb772 100644 --- a/packages/grafana-data/src/utils/namedColorsPalette.ts +++ b/packages/grafana-data/src/utils/namedColorsPalette.ts @@ -34,7 +34,8 @@ export type Color = | 'dark-purple' | 'semi-dark-purple' | 'light-purple' - | 'super-light-purple'; + | 'super-light-purple' + | 'panel-bg'; type ThemeVariants = { dark: string; @@ -82,6 +83,8 @@ export function buildColorsMapForTheme(theme: GrafanaTheme): Record { diff --git a/packages/grafana-data/src/utils/testdata/testTheme.ts b/packages/grafana-data/src/utils/testdata/testTheme.ts new file mode 100644 index 00000000000..f706e04e188 --- /dev/null +++ b/packages/grafana-data/src/utils/testdata/testTheme.ts @@ -0,0 +1,12 @@ +import { GrafanaTheme, GrafanaThemeType } from '../../types/theme'; + +export function getTestTheme(type: GrafanaThemeType = GrafanaThemeType.Dark): GrafanaTheme { + return ({ + type, + isDark: type === GrafanaThemeType.Dark, + isLight: type === GrafanaThemeType.Light, + colors: { + panelBg: 'white', + }, + } as unknown) as GrafanaTheme; +} diff --git a/packages/grafana-ui/src/components/BigValue/BigValueLayout.tsx b/packages/grafana-ui/src/components/BigValue/BigValueLayout.tsx index 9aac2e88d04..eabd079f09f 100644 --- a/packages/grafana-ui/src/components/BigValue/BigValueLayout.tsx +++ b/packages/grafana-ui/src/components/BigValue/BigValueLayout.tsx @@ -9,6 +9,7 @@ import { calculateFontSize } from '../../utils/measureText'; // Types import { BigValueColorMode, Props, BigValueJustifyMode, BigValueTextMode } from './BigValue'; +import { getTextColorForBackground } from '../../utils'; const LINE_HEIGHT = 1.2; const MAX_TITLE_SIZE = 30; @@ -51,7 +52,7 @@ export abstract class BigValueLayout { }; if (this.props.colorMode === BigValueColorMode.Background) { - styles.color = 'white'; + styles.color = getTextColorForBackground(this.valueColor); } return styles; @@ -69,7 +70,7 @@ export abstract class BigValueLayout { styles.color = this.valueColor; break; case BigValueColorMode.Background: - styles.color = 'white'; + styles.color = getTextColorForBackground(this.valueColor); } return styles; 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 63e3654e0f1..cfaf1b3416b 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 @@ -30,7 +30,7 @@ exports[`BigValue Render with basic options should render 1`] = ` { + let suffix = mode.isByValue ? ' (by value)' : ''; + return { value: mode.id, - label: mode.name, + label: `${mode.name}${suffix}`, description: mode.description, isContinuous: mode.isContinuous, isByValue: mode.isByValue, diff --git a/packages/grafana-ui/src/components/Table/DefaultCell.tsx b/packages/grafana-ui/src/components/Table/DefaultCell.tsx index bf9aa511b3f..1e16a472e9e 100644 --- a/packages/grafana-ui/src/components/Table/DefaultCell.tsx +++ b/packages/grafana-ui/src/components/Table/DefaultCell.tsx @@ -5,6 +5,7 @@ import { TableCellDisplayMode, TableCellProps } from './types'; import tinycolor from 'tinycolor2'; import { TableStyles } from './styles'; import { FilterActions } from './FilterActions'; +import { getTextColorForBackground } from '../../utils'; export const DefaultCell: FC = props => { const { field, cell, tableStyles, row, cellProps } = props; @@ -65,7 +66,12 @@ function getCellStyle(tableStyles: TableStyles, field: Field, displayValue: Disp .spin(5) .toRgbString(); - return tableStyles.buildCellContainerStyle('white', `linear-gradient(120deg, ${bgColor2}, ${displayValue.color})`); + const textColor = getTextColorForBackground(displayValue.color!); + + return tableStyles.buildCellContainerStyle( + textColor, + `linear-gradient(120deg, ${bgColor2}, ${displayValue.color})` + ); } return tableStyles.cellContainer; diff --git a/packages/grafana-ui/src/utils/colors.ts b/packages/grafana-ui/src/utils/colors.ts index a316919a9a8..15c393c885b 100644 --- a/packages/grafana-ui/src/utils/colors.ts +++ b/packages/grafana-ui/src/utils/colors.ts @@ -4,6 +4,8 @@ import flattenDeep from 'lodash/flattenDeep'; import chunk from 'lodash/chunk'; import zip from 'lodash/zip'; import tinycolor from 'tinycolor2'; +import lightTheme from '../themes/light'; +import darkTheme from '../themes/dark'; export const PALETTE_ROWS = 4; export const PALETTE_COLUMNS = 14; @@ -93,4 +95,9 @@ function hslToHex(color: any) { return tinycolor(color).toHexString(); } +export function getTextColorForBackground(color: string) { + const b = tinycolor(color).getBrightness(); + return b > 150 ? lightTheme.colors.textStrong : darkTheme.colors.textStrong; +} + export let sortedColors = sortColorsByHue(colors); diff --git a/packages/grafana-ui/src/utils/measureText.ts b/packages/grafana-ui/src/utils/measureText.ts index 0a267117a4b..2593d310e80 100644 --- a/packages/grafana-ui/src/utils/measureText.ts +++ b/packages/grafana-ui/src/utils/measureText.ts @@ -1,6 +1,9 @@ let canvas: HTMLCanvasElement | null = null; const cache: Record = {}; +/** + * @beta + */ export function measureText(text: string, fontSize: number): TextMetrics { const fontStyle = `${fontSize}px 'Roboto'`; const cacheKey = text + fontStyle; @@ -26,6 +29,9 @@ export function measureText(text: string, fontSize: number): TextMetrics { return metrics; } +/** + * @beta + */ export function calculateFontSize(text: string, width: number, height: number, lineHeight: number, maxSize?: number) { // calculate width in 14px const textSize = measureText(text, 14); diff --git a/public/app/features/panel/panellinks/linkSuppliers.test.ts b/public/app/features/panel/panellinks/linkSuppliers.test.ts index 374dfe8fcb8..941b83b320a 100644 --- a/public/app/features/panel/panellinks/linkSuppliers.test.ts +++ b/public/app/features/panel/panellinks/linkSuppliers.test.ts @@ -1,8 +1,9 @@ import { getFieldLinksSupplier } from './linkSuppliers'; -import { applyFieldOverrides, DataFrameView, dateTime, FieldDisplay, GrafanaTheme, toDataFrame } from '@grafana/data'; +import { applyFieldOverrides, DataFrameView, dateTime, FieldDisplay, toDataFrame } from '@grafana/data'; import { getLinkSrv, LinkService, LinkSrv, setLinkSrv } from './link_srv'; import { TemplateSrv } from '../../templating/template_srv'; import { TimeSrv } from '../../dashboard/services/TimeSrv'; +import { getTheme } from '@grafana/ui'; describe('getFieldLinksSupplier', () => { let originalLinkSrv: LinkService; @@ -91,7 +92,7 @@ describe('getFieldLinksSupplier', () => { replaceVariables: (val: string) => val, getDataSourceSettingsByUid: (val: string) => ({} as any), timeZone: 'utc', - theme: {} as GrafanaTheme, + theme: getTheme(), autoMinMax: true, })[0]; diff --git a/public/app/plugins/panel/table-old/specs/renderer.test.ts b/public/app/plugins/panel/table-old/specs/renderer.test.ts index caca84f279b..06176d2f15f 100644 --- a/public/app/plugins/panel/table-old/specs/renderer.test.ts +++ b/public/app/plugins/panel/table-old/specs/renderer.test.ts @@ -3,7 +3,7 @@ import TableModel from 'app/core/table_model'; import { TableRenderer } from '../renderer'; import { ScopedVars, TimeZone } from '@grafana/data'; import { ColumnRender } from '../types'; -import { config } from 'app/core/config'; +import { getTheme } from '@grafana/ui'; const utc: TimeZone = 'utc'; @@ -211,7 +211,7 @@ describe('when rendering table', () => { }; //@ts-ignore - const renderer = new TableRenderer(panel, table, utc, sanitize, templateSrv, config.theme); + const renderer = new TableRenderer(panel, table, utc, sanitize, templateSrv, getTheme()); it('time column should be formatted', () => { const html = renderer.renderCell(0, 0, 1388556366666); @@ -467,7 +467,7 @@ describe('when rendering table with different patterns', () => { }; //@ts-ignore - const renderer = new TableRenderer(panel, table, utc, sanitize, templateSrv); + const renderer = new TableRenderer(panel, table, utc, sanitize, templateSrv, getTheme()); const html = renderer.renderCell(1, 0, 1230); expect(html).toBe(expected); @@ -537,7 +537,7 @@ describe('when rendering cells with different alignment options', () => { }; //@ts-ignore - const renderer = new TableRenderer(panel, table, utc, sanitize, templateSrv); + const renderer = new TableRenderer(panel, table, utc, sanitize, templateSrv, getTheme()); const html = renderer.renderCell(1, 0, 42); expect(html).toBe(expected);