FieldType: Add enum type and include it in testdata scenarios (#64059)

This commit is contained in:
Ryan McKinley
2023-03-03 13:37:56 -08:00
committed by GitHub
parent aed020d9b5
commit b7d8589588
9 changed files with 200 additions and 89 deletions

View File

@@ -6,7 +6,7 @@ import { getFieldTypeFromValue } from '../dataframe/processDataFrame';
import { toUtc, dateTimeParse } from '../datetime';
import { GrafanaTheme2 } from '../themes/types';
import { KeyValue, TimeZone } from '../types';
import { Field, FieldType } from '../types/dataFrame';
import { EnumFieldConfig, Field, FieldType } from '../types/dataFrame';
import { DecimalCount, DisplayProcessor, DisplayValue } from '../types/displayValue';
import { anyToNumber } from '../utils/anyToNumber';
import { getValueMappingResult } from '../utils/valueMappings';
@@ -70,6 +70,8 @@ export function getDisplayProcessor(options?: DisplayProcessorOptions): DisplayP
}
} else if (!unit && field.type === FieldType.string) {
unit = 'string';
} else if (field.type === FieldType.enum) {
return getEnumDisplayProcessor(options.theme, config.type?.enum);
}
const hasCurrencyUnit = unit?.startsWith('currency');
@@ -190,6 +192,41 @@ function toStringProcessor(value: unknown): DisplayValue {
return { text: toString(value), numeric: anyToNumber(value) };
}
export function getEnumDisplayProcessor(theme: GrafanaTheme2, cfg?: EnumFieldConfig): DisplayProcessor {
const config = {
text: cfg?.text ?? [],
color: cfg?.color ?? [],
};
// use the theme specific color values
config.color = config.color.map((v) => theme.visualization.getColorByName(v));
return (value: unknown) => {
if (value == null) {
return {
text: '',
numeric: NaN,
};
}
const idx = +value;
let text = config.text[idx];
if (text == null) {
text = `${value}`; // the original value
}
let color = config.color[idx];
if (color == null) {
// constant color for index
const { palette } = theme.visualization;
color = palette[idx % palette.length];
config.color[idx] = color;
}
return {
text,
numeric: idx,
color,
};
};
}
export function getRawDisplayProcessor(): DisplayProcessor {
return (value: unknown) => ({
text: getFieldTypeFromValue(value) === 'other' ? `${JSON.stringify(value, getCircularReplacer())}` : `${value}`,

View File

@@ -2,7 +2,7 @@ import { map } from 'rxjs/operators';
import { dateTimeParse } from '../../datetime';
import { SynchronousDataTransformerInfo } from '../../types';
import { DataFrame, Field, FieldType } from '../../types/dataFrame';
import { DataFrame, EnumFieldConfig, Field, FieldType } from '../../types/dataFrame';
import { ArrayVector } from '../../vector';
import { fieldMatchers } from '../matchers';
import { FieldMatcherID } from '../matchers/ids';
@@ -26,6 +26,9 @@ export interface ConvertFieldTypeOptions {
* Date format to parse a string datetime
*/
dateFormat?: string;
/** When converting to an enumeration, this is the target config */
enumConfig?: EnumFieldConfig;
}
export const convertFieldTypeTransformer: SynchronousDataTransformerInfo<ConvertFieldTypeTransformerOptions> = {
@@ -44,11 +47,7 @@ export const convertFieldTypeTransformer: SynchronousDataTransformerInfo<Convert
if (!Array.isArray(data) || data.length === 0) {
return data;
}
const timeParsed = convertFieldTypes(options, data);
if (!timeParsed) {
return [];
}
return timeParsed;
return convertFieldTypes(options, data) ?? [];
},
};
@@ -101,6 +100,8 @@ export function convertFieldType(field: Field, opts: ConvertFieldTypeOptions): F
return fieldToStringField(field, opts.dateFormat);
case FieldType.boolean:
return fieldToBooleanField(field);
case FieldType.enum:
return fieldToEnumField(field, opts.enumConfig);
case FieldType.other:
return fieldToComplexField(field);
default:
@@ -241,3 +242,37 @@ export function ensureTimeField(field: Field, dateFormat?: string): Field {
}
return fieldToTimeField(field, dateFormat);
}
function fieldToEnumField(field: Field, cfg?: EnumFieldConfig): Field {
const enumConfig = { ...cfg };
const enumValues = field.values.toArray().slice();
const lookup = new Map<unknown, number>();
if (enumConfig.text) {
for (let i = 0; i < enumConfig.text.length; i++) {
lookup.set(enumConfig.text[i], i);
}
} else {
enumConfig.text = [];
}
for (let i = 0; i < enumValues.length; i++) {
const v = enumValues[i];
if (!lookup.has(v)) {
enumConfig.text[lookup.size] = v;
lookup.set(v, lookup.size);
}
enumValues[i] = lookup.get(v);
}
return {
...field,
config: {
...field.config,
type: {
enum: enumConfig,
},
},
type: FieldType.enum,
values: new ArrayVector(enumValues),
};
}

View File

@@ -16,6 +16,7 @@ export enum FieldType {
// Used to detect that the value is some kind of trace data to help with the visualisation and processing.
trace = 'trace',
geo = 'geo',
enum = 'enum',
other = 'other', // Object, Array, etc
}
@@ -91,10 +92,24 @@ export interface FieldConfig<TOptions = any> {
// Alternative to empty string
noValue?: string;
// The field type may map to specific config
type?: FieldTypeConfig;
// Panel Specific Values
custom?: TOptions;
}
export interface FieldTypeConfig {
enum?: EnumFieldConfig;
}
export interface EnumFieldConfig {
text?: string[];
color?: string[];
icon?: string[];
description?: string[];
}
/** @public */
export interface ValueLinkConfig {
/**

View File

@@ -147,6 +147,7 @@ export const availableIconsIndex = {
link: true,
'list-ui-alt': true,
'list-ul': true,
'list-ol': true,
lock: true,
'map-marker': true,
message: true,