mirror of
https://github.com/grafana/grafana.git
synced 2025-08-01 17:42:12 +08:00
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
This commit is contained in:
338
devenv/dev-dashboards/panel-common/color_modes.json
Normal file
338
devenv/dev-dashboards/panel-common/color_modes.json
Normal file
@ -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
|
||||||
|
}
|
@ -29,7 +29,7 @@ e2e.scenario({
|
|||||||
e2e.components.DashboardLinks.link()
|
e2e.components.DashboardLinks.link()
|
||||||
.should('be.visible')
|
.should('be.visible')
|
||||||
.and(links => {
|
.and(links => {
|
||||||
expect(links).to.have.length(13);
|
expect(links).to.have.length.greaterThan(13);
|
||||||
|
|
||||||
for (let index = 0; index < links.length; index++) {
|
for (let index = 0; index < links.length; index++) {
|
||||||
expect(Cypress.$(links[index]).attr('href')).contains(`var-custom=${variableValue}`);
|
expect(Cypress.$(links[index]).attr('href')).contains(`var-custom=${variableValue}`);
|
||||||
|
@ -3,13 +3,14 @@ import _ from 'lodash';
|
|||||||
|
|
||||||
// Types
|
// Types
|
||||||
import { Field, FieldType } from '../types/dataFrame';
|
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 { DecimalCount, DecimalInfo, DisplayProcessor, DisplayValue } from '../types/displayValue';
|
||||||
import { getValueFormat } from '../valueFormats/valueFormats';
|
import { getValueFormat } from '../valueFormats/valueFormats';
|
||||||
import { getMappedValue } from '../utils/valueMappings';
|
import { getMappedValue } from '../utils/valueMappings';
|
||||||
import { dateTime } from '../datetime';
|
import { dateTime } from '../datetime';
|
||||||
import { KeyValue, TimeZone } from '../types';
|
import { KeyValue, TimeZone } from '../types';
|
||||||
import { getScaleCalculator } from './scale';
|
import { getScaleCalculator } from './scale';
|
||||||
|
import { getTestTheme } from '../utils/testdata/testTheme';
|
||||||
|
|
||||||
interface DisplayProcessorOptions {
|
interface DisplayProcessorOptions {
|
||||||
field: Partial<Field>;
|
field: Partial<Field>;
|
||||||
@ -41,7 +42,7 @@ export function getDisplayProcessor(options?: DisplayProcessorOptions): DisplayP
|
|||||||
const config = field.config ?? {};
|
const config = field.config ?? {};
|
||||||
|
|
||||||
// Theme should be required or we need access to default theme instance from here
|
// 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 unit = config.unit;
|
||||||
let hasDateUnit = unit && (timeFormats[unit] || unit.startsWith('time:'));
|
let hasDateUnit = unit && (timeFormats[unit] || unit.startsWith('time:'));
|
||||||
|
@ -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';
|
import { fieldColorModeRegistry, FieldValueColorCalculator } from './fieldColor';
|
||||||
|
|
||||||
describe('fieldColorModeRegistry', () => {
|
describe('fieldColorModeRegistry', () => {
|
||||||
@ -9,10 +10,7 @@ describe('fieldColorModeRegistry', () => {
|
|||||||
|
|
||||||
function getCalculator(options: GetCalcOptions): FieldValueColorCalculator {
|
function getCalculator(options: GetCalcOptions): FieldValueColorCalculator {
|
||||||
const mode = fieldColorModeRegistry.get(options.mode);
|
const mode = fieldColorModeRegistry.get(options.mode);
|
||||||
return mode.getCalculator(
|
return mode.getCalculator({ state: { seriesIndex: options.seriesIndex } } as Field, getTestTheme());
|
||||||
{ state: { seriesIndex: options.seriesIndex } } as Field,
|
|
||||||
{ type: GrafanaThemeType.Dark } as GrafanaTheme
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
it('Schemes should interpolate', () => {
|
it('Schemes should interpolate', () => {
|
||||||
|
@ -54,20 +54,81 @@ export const fieldColorModeRegistry = new Registry<FieldColorMode>(() => {
|
|||||||
// }),
|
// }),
|
||||||
new FieldColorSchemeMode({
|
new FieldColorSchemeMode({
|
||||||
id: FieldColorModeId.PaletteClassic,
|
id: FieldColorModeId.PaletteClassic,
|
||||||
name: 'By series / Classic palette',
|
name: 'Classic palette',
|
||||||
//description: 'Assigns color based on series or field index',
|
|
||||||
isContinuous: false,
|
isContinuous: false,
|
||||||
isByValue: false,
|
isByValue: false,
|
||||||
colors: classicColors,
|
colors: classicColors,
|
||||||
}),
|
}),
|
||||||
new FieldColorSchemeMode({
|
new FieldColorSchemeMode({
|
||||||
id: 'continuous-GrYlRd',
|
id: 'continuous-GrYlRd',
|
||||||
name: 'By value / Green Yellow Red (gradient)',
|
name: 'Green-Yellow-Red',
|
||||||
//description: 'Interpolated colors based value, min and max',
|
|
||||||
isContinuous: true,
|
isContinuous: true,
|
||||||
isByValue: true,
|
isByValue: true,
|
||||||
colors: ['green', 'yellow', 'red'],
|
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'],
|
||||||
|
}),
|
||||||
];
|
];
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -2,9 +2,9 @@ import merge from 'lodash/merge';
|
|||||||
import { getFieldDisplayValues, GetFieldDisplayValuesOptions } from './fieldDisplay';
|
import { getFieldDisplayValues, GetFieldDisplayValuesOptions } from './fieldDisplay';
|
||||||
import { toDataFrame } from '../dataframe/processDataFrame';
|
import { toDataFrame } from '../dataframe/processDataFrame';
|
||||||
import { ReducerID } from '../transformations/fieldReducer';
|
import { ReducerID } from '../transformations/fieldReducer';
|
||||||
import { GrafanaTheme } from '../types/theme';
|
|
||||||
import { MappingType } from '../types';
|
import { MappingType } from '../types';
|
||||||
import { standardFieldConfigEditorRegistry } from './standardFieldConfigEditorRegistry';
|
import { standardFieldConfigEditorRegistry } from './standardFieldConfigEditorRegistry';
|
||||||
|
import { getTestTheme } from '../utils/testdata/testTheme';
|
||||||
|
|
||||||
describe('FieldDisplay', () => {
|
describe('FieldDisplay', () => {
|
||||||
beforeAll(() => {
|
beforeAll(() => {
|
||||||
@ -241,7 +241,7 @@ function createDisplayOptions(extend: Partial<GetFieldDisplayValuesOptions> = {}
|
|||||||
overrides: [],
|
overrides: [],
|
||||||
defaults: {},
|
defaults: {},
|
||||||
},
|
},
|
||||||
theme: {} as GrafanaTheme,
|
theme: getTestTheme(),
|
||||||
};
|
};
|
||||||
|
|
||||||
return merge<GetFieldDisplayValuesOptions, any>(options, extend);
|
return merge<GetFieldDisplayValuesOptions, any>(options, extend);
|
||||||
|
@ -15,7 +15,6 @@ import {
|
|||||||
FieldConfigPropertyItem,
|
FieldConfigPropertyItem,
|
||||||
FieldConfigSource,
|
FieldConfigSource,
|
||||||
FieldType,
|
FieldType,
|
||||||
GrafanaTheme,
|
|
||||||
InterpolateFunction,
|
InterpolateFunction,
|
||||||
ThresholdsMode,
|
ThresholdsMode,
|
||||||
FieldColorModeId,
|
FieldColorModeId,
|
||||||
@ -28,6 +27,7 @@ import { FieldConfigOptionsRegistry } from './FieldConfigOptionsRegistry';
|
|||||||
import { getFieldDisplayName } from './fieldState';
|
import { getFieldDisplayName } from './fieldState';
|
||||||
import { ArrayVector } from '../vector';
|
import { ArrayVector } from '../vector';
|
||||||
import { getDisplayProcessor } from './displayProcessor';
|
import { getDisplayProcessor } from './displayProcessor';
|
||||||
|
import { getTestTheme } from '../utils/testdata/testTheme';
|
||||||
|
|
||||||
const property1: any = {
|
const property1: any = {
|
||||||
id: 'custom.property1', // Match field properties
|
id: 'custom.property1', // Match field properties
|
||||||
@ -136,7 +136,7 @@ describe('applyFieldOverrides', () => {
|
|||||||
},
|
},
|
||||||
replaceVariables: (value: any) => value,
|
replaceVariables: (value: any) => value,
|
||||||
getDataSourceSettingsByUid: undefined as any,
|
getDataSourceSettingsByUid: undefined as any,
|
||||||
theme: {} as GrafanaTheme,
|
theme: getTestTheme(),
|
||||||
fieldConfigRegistry: new FieldConfigOptionsRegistry(),
|
fieldConfigRegistry: new FieldConfigOptionsRegistry(),
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -199,7 +199,7 @@ describe('applyFieldOverrides', () => {
|
|||||||
fieldConfigRegistry: customFieldRegistry,
|
fieldConfigRegistry: customFieldRegistry,
|
||||||
getDataSourceSettingsByUid: undefined as any,
|
getDataSourceSettingsByUid: undefined as any,
|
||||||
replaceVariables: v => v,
|
replaceVariables: v => v,
|
||||||
theme: {} as GrafanaTheme,
|
theme: getTestTheme(),
|
||||||
})[0];
|
})[0];
|
||||||
|
|
||||||
const outField = processed.fields[0];
|
const outField = processed.fields[0];
|
||||||
@ -216,7 +216,7 @@ describe('applyFieldOverrides', () => {
|
|||||||
fieldConfig: src as FieldConfigSource, // defaults + overrides
|
fieldConfig: src as FieldConfigSource, // defaults + overrides
|
||||||
replaceVariables: (undefined as any) as InterpolateFunction,
|
replaceVariables: (undefined as any) as InterpolateFunction,
|
||||||
getDataSourceSettingsByUid: undefined as any,
|
getDataSourceSettingsByUid: undefined as any,
|
||||||
theme: (undefined as any) as GrafanaTheme,
|
theme: getTestTheme(),
|
||||||
fieldConfigRegistry: customFieldRegistry,
|
fieldConfigRegistry: customFieldRegistry,
|
||||||
})[0];
|
})[0];
|
||||||
const valueColumn = data.fields[1];
|
const valueColumn = data.fields[1];
|
||||||
@ -244,7 +244,7 @@ describe('applyFieldOverrides', () => {
|
|||||||
fieldConfig: src as FieldConfigSource, // defaults + overrides
|
fieldConfig: src as FieldConfigSource, // defaults + overrides
|
||||||
replaceVariables: (undefined as any) as InterpolateFunction,
|
replaceVariables: (undefined as any) as InterpolateFunction,
|
||||||
getDataSourceSettingsByUid: undefined as any,
|
getDataSourceSettingsByUid: undefined as any,
|
||||||
theme: (undefined as any) as GrafanaTheme,
|
theme: getTestTheme(),
|
||||||
autoMinMax: true,
|
autoMinMax: true,
|
||||||
})[0];
|
})[0];
|
||||||
const valueColumn = data.fields[1];
|
const valueColumn = data.fields[1];
|
||||||
@ -268,7 +268,7 @@ describe('applyFieldOverrides', () => {
|
|||||||
return value;
|
return value;
|
||||||
}) as InterpolateFunction,
|
}) as InterpolateFunction,
|
||||||
getDataSourceSettingsByUid: undefined as any,
|
getDataSourceSettingsByUid: undefined as any,
|
||||||
theme: (undefined as any) as GrafanaTheme,
|
theme: getTestTheme(),
|
||||||
autoMinMax: true,
|
autoMinMax: true,
|
||||||
fieldConfigRegistry: customFieldRegistry,
|
fieldConfigRegistry: customFieldRegistry,
|
||||||
})[0];
|
})[0];
|
||||||
@ -521,7 +521,7 @@ describe('getLinksSupplier', () => {
|
|||||||
// this is used only for internal links so isn't needed here
|
// this is used only for internal links so isn't needed here
|
||||||
() => ({} as any),
|
() => ({} as any),
|
||||||
{
|
{
|
||||||
theme: {} as GrafanaTheme,
|
theme: getTestTheme(),
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
supplier({});
|
supplier({});
|
||||||
@ -568,7 +568,7 @@ describe('getLinksSupplier', () => {
|
|||||||
// We do not need to interpolate anything for this test
|
// We do not need to interpolate anything for this test
|
||||||
(value, vars, format) => value,
|
(value, vars, format) => value,
|
||||||
uid => ({ name: 'testDS' } as any),
|
uid => ({ name: 'testDS' } as any),
|
||||||
{ theme: {} as GrafanaTheme }
|
{ theme: getTestTheme() }
|
||||||
);
|
);
|
||||||
const links = supplier({ valueRowIndex: 0 });
|
const links = supplier({ valueRowIndex: 0 });
|
||||||
expect(links.length).toBe(1);
|
expect(links.length).toBe(1);
|
||||||
|
@ -2,6 +2,7 @@ import { getFieldDisplayValuesProxy } from './getFieldDisplayValuesProxy';
|
|||||||
import { applyFieldOverrides } from './fieldOverrides';
|
import { applyFieldOverrides } from './fieldOverrides';
|
||||||
import { toDataFrame } from '../dataframe';
|
import { toDataFrame } from '../dataframe';
|
||||||
import { GrafanaTheme } from '../types';
|
import { GrafanaTheme } from '../types';
|
||||||
|
import { getTestTheme } from '../utils/testdata/testTheme';
|
||||||
|
|
||||||
describe('getFieldDisplayValuesProxy', () => {
|
describe('getFieldDisplayValuesProxy', () => {
|
||||||
const data = applyFieldOverrides({
|
const data = applyFieldOverrides({
|
||||||
@ -30,7 +31,7 @@ describe('getFieldDisplayValuesProxy', () => {
|
|||||||
replaceVariables: (val: string) => val,
|
replaceVariables: (val: string) => val,
|
||||||
getDataSourceSettingsByUid: (val: string) => ({} as any),
|
getDataSourceSettingsByUid: (val: string) => ({} as any),
|
||||||
timeZone: 'utc',
|
timeZone: 'utc',
|
||||||
theme: {} as GrafanaTheme,
|
theme: getTestTheme(),
|
||||||
autoMinMax: true,
|
autoMinMax: true,
|
||||||
})[0];
|
})[0];
|
||||||
|
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
import { ThresholdsMode, Field, FieldType, GrafanaThemeType, GrafanaTheme } from '../types';
|
import { ThresholdsMode, Field, FieldType } from '../types';
|
||||||
import { sortThresholds } from './thresholds';
|
import { sortThresholds } from './thresholds';
|
||||||
import { ArrayVector } from '../vector/ArrayVector';
|
import { ArrayVector } from '../vector/ArrayVector';
|
||||||
import { getScaleCalculator } from './scale';
|
import { getScaleCalculator } from './scale';
|
||||||
|
import { getTestTheme } from '../utils/testdata/testTheme';
|
||||||
|
|
||||||
describe('getScaleCalculator', () => {
|
describe('getScaleCalculator', () => {
|
||||||
it('should return percent, threshold and color', () => {
|
it('should return percent, threshold and color', () => {
|
||||||
@ -18,7 +19,7 @@ describe('getScaleCalculator', () => {
|
|||||||
values: new ArrayVector([0, 50, 100]),
|
values: new ArrayVector([0, 50, 100]),
|
||||||
};
|
};
|
||||||
|
|
||||||
const calc = getScaleCalculator(field, { type: GrafanaThemeType.Dark } as GrafanaTheme);
|
const calc = getScaleCalculator(field, getTestTheme());
|
||||||
expect(calc(70)).toEqual({
|
expect(calc(70)).toEqual({
|
||||||
percent: 0.7,
|
percent: 0.7,
|
||||||
threshold: thresholds[1],
|
threshold: thresholds[1],
|
||||||
|
@ -34,7 +34,8 @@ export type Color =
|
|||||||
| 'dark-purple'
|
| 'dark-purple'
|
||||||
| 'semi-dark-purple'
|
| 'semi-dark-purple'
|
||||||
| 'light-purple'
|
| 'light-purple'
|
||||||
| 'super-light-purple';
|
| 'super-light-purple'
|
||||||
|
| 'panel-bg';
|
||||||
|
|
||||||
type ThemeVariants = {
|
type ThemeVariants = {
|
||||||
dark: string;
|
dark: string;
|
||||||
@ -82,6 +83,8 @@ export function buildColorsMapForTheme(theme: GrafanaTheme): Record<Color, strin
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
colorsMap['panel-bg'] = theme.colors.panelBg;
|
||||||
|
|
||||||
return colorsMap;
|
return colorsMap;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -118,7 +121,25 @@ export function getColorForTheme(color: string, theme: GrafanaTheme): string {
|
|||||||
export function getColorFromHexRgbOrName(color: string, type?: GrafanaThemeType): string {
|
export function getColorFromHexRgbOrName(color: string, type?: GrafanaThemeType): string {
|
||||||
const themeType = type ?? GrafanaThemeType.Dark;
|
const themeType = type ?? GrafanaThemeType.Dark;
|
||||||
|
|
||||||
return getColorForTheme(color, ({ type: themeType } as unknown) as GrafanaTheme);
|
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
const buildNamedColorsPalette = () => {
|
const buildNamedColorsPalette = () => {
|
||||||
|
12
packages/grafana-data/src/utils/testdata/testTheme.ts
vendored
Normal file
12
packages/grafana-data/src/utils/testdata/testTheme.ts
vendored
Normal file
@ -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;
|
||||||
|
}
|
@ -9,6 +9,7 @@ import { calculateFontSize } from '../../utils/measureText';
|
|||||||
|
|
||||||
// Types
|
// Types
|
||||||
import { BigValueColorMode, Props, BigValueJustifyMode, BigValueTextMode } from './BigValue';
|
import { BigValueColorMode, Props, BigValueJustifyMode, BigValueTextMode } from './BigValue';
|
||||||
|
import { getTextColorForBackground } from '../../utils';
|
||||||
|
|
||||||
const LINE_HEIGHT = 1.2;
|
const LINE_HEIGHT = 1.2;
|
||||||
const MAX_TITLE_SIZE = 30;
|
const MAX_TITLE_SIZE = 30;
|
||||||
@ -51,7 +52,7 @@ export abstract class BigValueLayout {
|
|||||||
};
|
};
|
||||||
|
|
||||||
if (this.props.colorMode === BigValueColorMode.Background) {
|
if (this.props.colorMode === BigValueColorMode.Background) {
|
||||||
styles.color = 'white';
|
styles.color = getTextColorForBackground(this.valueColor);
|
||||||
}
|
}
|
||||||
|
|
||||||
return styles;
|
return styles;
|
||||||
@ -69,7 +70,7 @@ export abstract class BigValueLayout {
|
|||||||
styles.color = this.valueColor;
|
styles.color = this.valueColor;
|
||||||
break;
|
break;
|
||||||
case BigValueColorMode.Background:
|
case BigValueColorMode.Background:
|
||||||
styles.color = 'white';
|
styles.color = getTextColorForBackground(this.valueColor);
|
||||||
}
|
}
|
||||||
|
|
||||||
return styles;
|
return styles;
|
||||||
|
@ -30,7 +30,7 @@ exports[`BigValue Render with basic options should render 1`] = `
|
|||||||
<FormattedDisplayValue
|
<FormattedDisplayValue
|
||||||
style={
|
style={
|
||||||
Object {
|
Object {
|
||||||
"color": "white",
|
"color": "#202226",
|
||||||
"fontSize": 230,
|
"fontSize": 230,
|
||||||
"fontWeight": 500,
|
"fontWeight": 500,
|
||||||
"lineHeight": 1.2,
|
"lineHeight": 1.2,
|
||||||
|
@ -23,9 +23,11 @@ export const FieldColorEditor: React.FC<FieldConfigEditorProps<FieldColor | unde
|
|||||||
const styles = useStyles(getStyles);
|
const styles = useStyles(getStyles);
|
||||||
|
|
||||||
const options = fieldColorModeRegistry.list().map(mode => {
|
const options = fieldColorModeRegistry.list().map(mode => {
|
||||||
|
let suffix = mode.isByValue ? ' (by value)' : '';
|
||||||
|
|
||||||
return {
|
return {
|
||||||
value: mode.id,
|
value: mode.id,
|
||||||
label: mode.name,
|
label: `${mode.name}${suffix}`,
|
||||||
description: mode.description,
|
description: mode.description,
|
||||||
isContinuous: mode.isContinuous,
|
isContinuous: mode.isContinuous,
|
||||||
isByValue: mode.isByValue,
|
isByValue: mode.isByValue,
|
||||||
|
@ -5,6 +5,7 @@ import { TableCellDisplayMode, TableCellProps } from './types';
|
|||||||
import tinycolor from 'tinycolor2';
|
import tinycolor from 'tinycolor2';
|
||||||
import { TableStyles } from './styles';
|
import { TableStyles } from './styles';
|
||||||
import { FilterActions } from './FilterActions';
|
import { FilterActions } from './FilterActions';
|
||||||
|
import { getTextColorForBackground } from '../../utils';
|
||||||
|
|
||||||
export const DefaultCell: FC<TableCellProps> = props => {
|
export const DefaultCell: FC<TableCellProps> = props => {
|
||||||
const { field, cell, tableStyles, row, cellProps } = props;
|
const { field, cell, tableStyles, row, cellProps } = props;
|
||||||
@ -65,7 +66,12 @@ function getCellStyle(tableStyles: TableStyles, field: Field, displayValue: Disp
|
|||||||
.spin(5)
|
.spin(5)
|
||||||
.toRgbString();
|
.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;
|
return tableStyles.cellContainer;
|
||||||
|
@ -4,6 +4,8 @@ import flattenDeep from 'lodash/flattenDeep';
|
|||||||
import chunk from 'lodash/chunk';
|
import chunk from 'lodash/chunk';
|
||||||
import zip from 'lodash/zip';
|
import zip from 'lodash/zip';
|
||||||
import tinycolor from 'tinycolor2';
|
import tinycolor from 'tinycolor2';
|
||||||
|
import lightTheme from '../themes/light';
|
||||||
|
import darkTheme from '../themes/dark';
|
||||||
|
|
||||||
export const PALETTE_ROWS = 4;
|
export const PALETTE_ROWS = 4;
|
||||||
export const PALETTE_COLUMNS = 14;
|
export const PALETTE_COLUMNS = 14;
|
||||||
@ -93,4 +95,9 @@ function hslToHex(color: any) {
|
|||||||
return tinycolor(color).toHexString();
|
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);
|
export let sortedColors = sortColorsByHue(colors);
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
let canvas: HTMLCanvasElement | null = null;
|
let canvas: HTMLCanvasElement | null = null;
|
||||||
const cache: Record<string, TextMetrics> = {};
|
const cache: Record<string, TextMetrics> = {};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @beta
|
||||||
|
*/
|
||||||
export function measureText(text: string, fontSize: number): TextMetrics {
|
export function measureText(text: string, fontSize: number): TextMetrics {
|
||||||
const fontStyle = `${fontSize}px 'Roboto'`;
|
const fontStyle = `${fontSize}px 'Roboto'`;
|
||||||
const cacheKey = text + fontStyle;
|
const cacheKey = text + fontStyle;
|
||||||
@ -26,6 +29,9 @@ export function measureText(text: string, fontSize: number): TextMetrics {
|
|||||||
return metrics;
|
return metrics;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @beta
|
||||||
|
*/
|
||||||
export function calculateFontSize(text: string, width: number, height: number, lineHeight: number, maxSize?: number) {
|
export function calculateFontSize(text: string, width: number, height: number, lineHeight: number, maxSize?: number) {
|
||||||
// calculate width in 14px
|
// calculate width in 14px
|
||||||
const textSize = measureText(text, 14);
|
const textSize = measureText(text, 14);
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
import { getFieldLinksSupplier } from './linkSuppliers';
|
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 { getLinkSrv, LinkService, LinkSrv, setLinkSrv } from './link_srv';
|
||||||
import { TemplateSrv } from '../../templating/template_srv';
|
import { TemplateSrv } from '../../templating/template_srv';
|
||||||
import { TimeSrv } from '../../dashboard/services/TimeSrv';
|
import { TimeSrv } from '../../dashboard/services/TimeSrv';
|
||||||
|
import { getTheme } from '@grafana/ui';
|
||||||
|
|
||||||
describe('getFieldLinksSupplier', () => {
|
describe('getFieldLinksSupplier', () => {
|
||||||
let originalLinkSrv: LinkService;
|
let originalLinkSrv: LinkService;
|
||||||
@ -91,7 +92,7 @@ describe('getFieldLinksSupplier', () => {
|
|||||||
replaceVariables: (val: string) => val,
|
replaceVariables: (val: string) => val,
|
||||||
getDataSourceSettingsByUid: (val: string) => ({} as any),
|
getDataSourceSettingsByUid: (val: string) => ({} as any),
|
||||||
timeZone: 'utc',
|
timeZone: 'utc',
|
||||||
theme: {} as GrafanaTheme,
|
theme: getTheme(),
|
||||||
autoMinMax: true,
|
autoMinMax: true,
|
||||||
})[0];
|
})[0];
|
||||||
|
|
||||||
|
@ -3,7 +3,7 @@ import TableModel from 'app/core/table_model';
|
|||||||
import { TableRenderer } from '../renderer';
|
import { TableRenderer } from '../renderer';
|
||||||
import { ScopedVars, TimeZone } from '@grafana/data';
|
import { ScopedVars, TimeZone } from '@grafana/data';
|
||||||
import { ColumnRender } from '../types';
|
import { ColumnRender } from '../types';
|
||||||
import { config } from 'app/core/config';
|
import { getTheme } from '@grafana/ui';
|
||||||
|
|
||||||
const utc: TimeZone = 'utc';
|
const utc: TimeZone = 'utc';
|
||||||
|
|
||||||
@ -211,7 +211,7 @@ describe('when rendering table', () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
//@ts-ignore
|
//@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', () => {
|
it('time column should be formatted', () => {
|
||||||
const html = renderer.renderCell(0, 0, 1388556366666);
|
const html = renderer.renderCell(0, 0, 1388556366666);
|
||||||
@ -467,7 +467,7 @@ describe('when rendering table with different patterns', () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
//@ts-ignore
|
//@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);
|
const html = renderer.renderCell(1, 0, 1230);
|
||||||
|
|
||||||
expect(html).toBe(expected);
|
expect(html).toBe(expected);
|
||||||
@ -537,7 +537,7 @@ describe('when rendering cells with different alignment options', () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
//@ts-ignore
|
//@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);
|
const html = renderer.renderCell(1, 0, 42);
|
||||||
|
|
||||||
expect(html).toBe(expected);
|
expect(html).toBe(expected);
|
||||||
|
Reference in New Issue
Block a user