OptionsUI: add standard field name picker (#36732)

This commit is contained in:
Ryan McKinley
2021-07-14 11:54:58 -07:00
committed by GitHub
parent 2f595fa144
commit 6d87b26d6c
6 changed files with 140 additions and 11 deletions

View File

@@ -1,4 +1,5 @@
import { DataLink, FieldOverrideContext, SelectableValue, ThresholdsConfig, ValueMapping } from '../../types';
import { ComponentType } from 'react';
import { DataLink, Field, FieldOverrideContext, SelectableValue, ThresholdsConfig, ValueMapping } from '../../types';
export const identityOverrideProcessor = <T>(value: T, _context: FieldOverrideContext, _settings: any) => {
return value;
@@ -158,3 +159,27 @@ export interface StatsPickerConfigSettings {
*/
defaultStat?: string;
}
interface FieldNamePickerInfoProps {
name?: string;
field?: Field;
}
export interface FieldNamePickerConfigSettings {
/**
* Function is a predicate, to test each element of the array.
* Return a value that coerces to true to keep the field, or to false otherwise.
*/
filter?: (field: Field) => boolean;
/**
* Show this text when no values are found
*/
noFieldsMessage?: string;
/**
* When a field is selected, this component can show aditional
* information, including validation etc
*/
info?: ComponentType<FieldNamePickerInfoProps> | null;
}

View File

@@ -15,6 +15,7 @@ import {
identityOverrideProcessor,
UnitFieldConfigSettings,
unitOverrideProcessor,
FieldNamePickerConfigSettings,
} from '../field';
/**
@@ -235,4 +236,14 @@ export class PanelOptionsEditorBuilder<TOptions> extends OptionsUIRegistryBuilde
editor: standardEditorsRegistry.get('unit').editor as any,
});
}
addFieldNamePicker<TSettings = any>(
config: PanelOptionsEditorConfig<TOptions, TSettings & FieldNamePickerConfigSettings, string>
): this {
return this.addCustomEditor({
...config,
id: config.path,
editor: standardEditorsRegistry.get('field-name').editor as any,
});
}
}

View File

@@ -0,0 +1,39 @@
import React, { useCallback } from 'react';
import { FieldNamePickerConfigSettings, SelectableValue, StandardEditorProps } from '@grafana/data';
import { Select } from '../Select/Select';
import { useFieldDisplayNames, useSelectOptions, frameHasName } from './utils';
// Pick a field name out of the fulds
export const FieldNamePicker: React.FC<StandardEditorProps<string, FieldNamePickerConfigSettings>> = ({
value,
onChange,
context,
item,
}) => {
const settings: FieldNamePickerConfigSettings = item.settings ?? {};
const names = useFieldDisplayNames(context.data, settings?.filter);
const selectOptions = useSelectOptions(names, value);
const onSelectChange = useCallback(
(selection: SelectableValue<string>) => {
if (!frameHasName(selection.value, names)) {
return;
}
return onChange(selection.value!);
},
[names, onChange]
);
const selectedOption = selectOptions.find((v) => v.value === value);
return (
<>
<Select
value={selectedOption}
options={selectOptions}
onChange={onSelectChange}
noOptionsMessage={settings.noFieldsMessage}
/>
{settings.info && <settings.info name={value} field={names.fields.get(value)} />}
</>
);
};

View File

@@ -1,5 +1,5 @@
import { useMemo } from 'react';
import { DataFrame, getFieldDisplayName, SelectableValue } from '@grafana/data';
import { DataFrame, Field, getFieldDisplayName, SelectableValue } from '@grafana/data';
/**
* @internal
@@ -10,6 +10,9 @@ export interface FrameFieldsDisplayNames {
// raw field names (that are explicitly not visible)
raw: Set<string>;
// Field mappings (duplicates are not supported)
fields: Map<string, Field>;
}
/**
@@ -25,18 +28,24 @@ export function frameHasName(name: string | undefined, names: FrameFieldsDisplay
/**
* Retuns the distinct names in a set of frames
*/
function getFrameFieldsDisplayNames(data: DataFrame[]): FrameFieldsDisplayNames {
function getFrameFieldsDisplayNames(data: DataFrame[], filter?: (field: Field) => boolean): FrameFieldsDisplayNames {
const names: FrameFieldsDisplayNames = {
display: new Set<string>(),
raw: new Set<string>(),
fields: new Map<string, Field>(),
};
for (const frame of data) {
for (const field of frame.fields) {
if (filter && !filter(field)) {
continue;
}
const disp = getFieldDisplayName(field, frame, data);
names.display.add(disp);
names.fields.set(disp, field);
if (field.name && disp !== field.name) {
names.raw.add(field.name);
names.fields.set(field.name, field);
}
}
}
@@ -46,10 +55,10 @@ function getFrameFieldsDisplayNames(data: DataFrame[]): FrameFieldsDisplayNames
/**
* @internal
*/
export function useFieldDisplayNames(data: DataFrame[]): FrameFieldsDisplayNames {
export function useFieldDisplayNames(data: DataFrame[], filter?: (field: Field) => boolean): FrameFieldsDisplayNames {
return useMemo(() => {
return getFrameFieldsDisplayNames(data);
}, [data]);
return getFrameFieldsDisplayNames(data, filter);
}, [data, filter]);
}
/**

View File

@@ -23,6 +23,7 @@ import {
FieldColorConfigSettings,
StatsPickerConfigSettings,
displayNameOverrideProcessor,
FieldNamePickerConfigSettings,
} from '@grafana/data';
import { Switch } from '../components/Switch/Switch';
@@ -43,6 +44,7 @@ import { DataLinksValueEditor } from '../components/OptionsUI/links';
import { ColorValueEditor } from '../components/OptionsUI/color';
import { FieldColorEditor } from '../components/OptionsUI/fieldColor';
import { StatsPickerEditor } from '../components/OptionsUI/stats';
import { FieldNamePicker } from '../components/MatchersUI/FieldNamePicker';
/**
* Returns collection of common field config properties definitions
@@ -347,6 +349,13 @@ export const getStandardOptionEditors = () => {
editor: TimeZonePicker as any,
};
const fieldName: StandardEditorsRegistryItem<string, FieldNamePickerConfigSettings> = {
id: 'field-name',
name: 'Field name',
description: 'Time zone selection',
editor: FieldNamePicker as any,
};
return [
text,
number,
@@ -364,5 +373,6 @@ export const getStandardOptionEditors = () => {
fieldColor,
color,
multiSelect,
fieldName,
];
};