mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
FieldConfig: Unify the custom and standard registry (#23307)
* FieldConfig: Unifying standard and custom registry * Adding path to option items to make id be prefixed for custom options * Code updates progress * Add docs back * Fix TS * ld overrides tests from ui to data * Refactor - rename * Gauge and table cleanup * F-I-X e2e Co-authored-by: Dominik Prokop <dominik.prokop@grafana.com>
This commit is contained in:
@@ -181,7 +181,7 @@ export class PanelInspector extends PureComponent<Props, State> {
|
||||
const processed = applyFieldOverrides({
|
||||
data,
|
||||
theme: config.theme,
|
||||
fieldOptions: { defaults: {}, overrides: [] },
|
||||
fieldConfig: { defaults: {}, overrides: [] },
|
||||
replaceVariables: (value: string) => {
|
||||
return value;
|
||||
},
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import React from 'react';
|
||||
import { DynamicConfigValue, FieldConfigEditorRegistry, FieldOverrideContext, GrafanaTheme } from '@grafana/data';
|
||||
import { DynamicConfigValue, FieldConfigOptionsRegistry, FieldOverrideContext, GrafanaTheme } from '@grafana/data';
|
||||
import { FieldConfigItemHeaderTitle, selectThemeVariant, stylesFactory, useTheme } from '@grafana/ui';
|
||||
|
||||
import { css } from 'emotion';
|
||||
interface DynamicConfigValueEditorProps {
|
||||
property: DynamicConfigValue;
|
||||
editorsRegistry: FieldConfigEditorRegistry;
|
||||
registry: FieldConfigOptionsRegistry;
|
||||
onChange: (value: DynamicConfigValue) => void;
|
||||
context: FieldOverrideContext;
|
||||
onRemove: () => void;
|
||||
@@ -14,13 +14,13 @@ interface DynamicConfigValueEditorProps {
|
||||
export const DynamicConfigValueEditor: React.FC<DynamicConfigValueEditorProps> = ({
|
||||
property,
|
||||
context,
|
||||
editorsRegistry,
|
||||
registry,
|
||||
onChange,
|
||||
onRemove,
|
||||
}) => {
|
||||
const theme = useTheme();
|
||||
const styles = getStyles(theme);
|
||||
const item = editorsRegistry?.getIfExists(property.prop);
|
||||
const item = registry?.getIfExists(property.id);
|
||||
|
||||
if (!item) {
|
||||
return null;
|
||||
|
||||
@@ -5,10 +5,8 @@ import {
|
||||
DataFrame,
|
||||
FieldPropertyEditorItem,
|
||||
VariableSuggestionsScope,
|
||||
standardFieldConfigEditorRegistry,
|
||||
PanelPlugin,
|
||||
SelectableValue,
|
||||
FieldConfigProperty,
|
||||
} from '@grafana/data';
|
||||
import { Forms, fieldMatchersUI, ValuePicker, useTheme } from '@grafana/ui';
|
||||
import { getDataLinksVariableSuggestions } from '../../../panel/panellinks/link_srv';
|
||||
@@ -18,7 +16,6 @@ import { css } from 'emotion';
|
||||
interface Props {
|
||||
plugin: PanelPlugin;
|
||||
config: FieldConfigSource;
|
||||
include?: FieldConfigProperty[]; // Ordered list of which fields should be shown/included
|
||||
onChange: (config: FieldConfigSource) => void;
|
||||
/* Helpful for IntelliSense */
|
||||
data: DataFrame[];
|
||||
@@ -62,33 +59,12 @@ export const OverrideFieldConfigEditor: React.FC<Props> = props => {
|
||||
|
||||
const renderOverrides = () => {
|
||||
const { config, data, plugin } = props;
|
||||
const { customFieldConfigs } = plugin;
|
||||
const { fieldConfigRegistry } = plugin;
|
||||
|
||||
if (config.overrides.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
let configPropertiesOptions = plugin.standardFieldConfigProperties.map(i => {
|
||||
const editor = standardFieldConfigEditorRegistry.get(i);
|
||||
return {
|
||||
label: editor.name,
|
||||
value: editor.id,
|
||||
description: editor.description,
|
||||
custom: false,
|
||||
};
|
||||
});
|
||||
|
||||
if (customFieldConfigs) {
|
||||
configPropertiesOptions = configPropertiesOptions.concat(
|
||||
customFieldConfigs.list().map(i => ({
|
||||
label: i.name,
|
||||
value: i.id,
|
||||
description: i.description,
|
||||
custom: true,
|
||||
}))
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
{config.overrides.map((o, i) => {
|
||||
@@ -100,8 +76,7 @@ export const OverrideFieldConfigEditor: React.FC<Props> = props => {
|
||||
override={o}
|
||||
onChange={value => onOverrideChange(i, value)}
|
||||
onRemove={() => onOverrideRemove(i)}
|
||||
configPropertiesOptions={configPropertiesOptions}
|
||||
customPropertiesRegistry={customFieldConfigs}
|
||||
registry={fieldConfigRegistry}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
@@ -135,7 +110,7 @@ export const OverrideFieldConfigEditor: React.FC<Props> = props => {
|
||||
);
|
||||
};
|
||||
|
||||
export const DefaultFieldConfigEditor: React.FC<Props> = ({ include, data, onChange, config, plugin }) => {
|
||||
export const DefaultFieldConfigEditor: React.FC<Props> = ({ data, onChange, config, plugin }) => {
|
||||
const setDefaultValue = useCallback(
|
||||
(name: string, value: any, custom: boolean) => {
|
||||
const defaults = { ...config.defaults };
|
||||
@@ -167,16 +142,20 @@ export const DefaultFieldConfigEditor: React.FC<Props> = ({ include, data, onCha
|
||||
);
|
||||
|
||||
const renderEditor = useCallback(
|
||||
(item: FieldPropertyEditorItem, custom: boolean) => {
|
||||
(item: FieldPropertyEditorItem) => {
|
||||
const defaults = config.defaults;
|
||||
const value = custom ? (defaults.custom ? defaults.custom[item.id] : undefined) : (defaults as any)[item.id];
|
||||
const value = item.isCustom
|
||||
? defaults.custom
|
||||
? defaults.custom[item.path]
|
||||
: undefined
|
||||
: (defaults as any)[item.path];
|
||||
|
||||
return (
|
||||
<Forms.Field label={item.name} description={item.description} key={`${item.id}/${custom}`}>
|
||||
<Forms.Field label={item.name} description={item.description} key={`${item.id}`}>
|
||||
<item.editor
|
||||
item={item}
|
||||
value={value}
|
||||
onChange={v => setDefaultValue(item.id, v, custom)}
|
||||
onChange={v => setDefaultValue(item.path, v, item.isCustom)}
|
||||
context={{
|
||||
data,
|
||||
getSuggestions: (scope?: VariableSuggestionsScope) => getDataLinksVariableSuggestions(data, scope),
|
||||
@@ -188,28 +167,6 @@ export const DefaultFieldConfigEditor: React.FC<Props> = ({ include, data, onCha
|
||||
[config]
|
||||
);
|
||||
|
||||
const renderStandardConfigs = useCallback(() => {
|
||||
if (include && include.length === 0) {
|
||||
return null;
|
||||
}
|
||||
if (include) {
|
||||
return <>{include.map(f => renderEditor(standardFieldConfigEditorRegistry.get(f), false))}</>;
|
||||
}
|
||||
return <>{standardFieldConfigEditorRegistry.list().map(f => renderEditor(f, false))}</>;
|
||||
}, [plugin, config]);
|
||||
|
||||
const renderCustomConfigs = useCallback(() => {
|
||||
if (!plugin.customFieldConfigs) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return plugin.customFieldConfigs.list().map(f => renderEditor(f, true));
|
||||
}, [plugin, config]);
|
||||
|
||||
return (
|
||||
<>
|
||||
{plugin.customFieldConfigs && renderCustomConfigs()}
|
||||
{renderStandardConfigs()}
|
||||
</>
|
||||
);
|
||||
// render all field configs
|
||||
return <>{plugin.fieldConfigRegistry.list().map(renderEditor)}</>;
|
||||
};
|
||||
|
||||
@@ -60,7 +60,6 @@ export const OptionsPaneContent: React.FC<{
|
||||
plugin={plugin}
|
||||
onChange={onFieldConfigsChange}
|
||||
data={data.series}
|
||||
include={plugin.standardFieldConfigProperties}
|
||||
/>
|
||||
</Container>
|
||||
);
|
||||
|
||||
@@ -3,10 +3,8 @@ import {
|
||||
ConfigOverrideRule,
|
||||
DataFrame,
|
||||
DynamicConfigValue,
|
||||
FieldConfigEditorRegistry,
|
||||
standardFieldConfigEditorRegistry,
|
||||
FieldConfigOptionsRegistry,
|
||||
VariableSuggestionsScope,
|
||||
SelectableValue,
|
||||
GrafanaTheme,
|
||||
} from '@grafana/data';
|
||||
import { fieldMatchersUI, stylesFactory, useTheme, ValuePicker, selectThemeVariant } from '@grafana/ui';
|
||||
@@ -21,18 +19,10 @@ interface OverrideEditorProps {
|
||||
override: ConfigOverrideRule;
|
||||
onChange: (config: ConfigOverrideRule) => void;
|
||||
onRemove: () => void;
|
||||
customPropertiesRegistry?: FieldConfigEditorRegistry;
|
||||
configPropertiesOptions: Array<SelectableValue<string>>;
|
||||
registry: FieldConfigOptionsRegistry;
|
||||
}
|
||||
|
||||
export const OverrideEditor: React.FC<OverrideEditorProps> = ({
|
||||
data,
|
||||
override,
|
||||
onChange,
|
||||
onRemove,
|
||||
customPropertiesRegistry,
|
||||
configPropertiesOptions,
|
||||
}) => {
|
||||
export const OverrideEditor: React.FC<OverrideEditorProps> = ({ data, override, onChange, onRemove, registry }) => {
|
||||
const theme = useTheme();
|
||||
const onMatcherConfigChange = useCallback(
|
||||
(matcherConfig: any) => {
|
||||
@@ -59,10 +49,10 @@ export const OverrideEditor: React.FC<OverrideEditorProps> = ({
|
||||
);
|
||||
|
||||
const onDynamicConfigValueAdd = useCallback(
|
||||
(prop: string, custom?: boolean) => {
|
||||
(id: string, custom?: boolean) => {
|
||||
const propertyConfig: DynamicConfigValue = {
|
||||
prop,
|
||||
custom,
|
||||
id,
|
||||
isCustom: custom,
|
||||
};
|
||||
if (override.properties) {
|
||||
override.properties.push(propertyConfig);
|
||||
@@ -74,6 +64,15 @@ export const OverrideEditor: React.FC<OverrideEditorProps> = ({
|
||||
[override, onChange]
|
||||
);
|
||||
|
||||
let configPropertiesOptions = registry.list().map(item => {
|
||||
return {
|
||||
label: item.name,
|
||||
value: item.id,
|
||||
description: item.description,
|
||||
custom: item.isCustom,
|
||||
};
|
||||
});
|
||||
|
||||
const matcherUi = fieldMatchersUI.get(override.matcher.id);
|
||||
const styles = getStyles(theme);
|
||||
return (
|
||||
@@ -90,20 +89,19 @@ export const OverrideEditor: React.FC<OverrideEditorProps> = ({
|
||||
</FieldConfigItemHeaderTitle>
|
||||
<div>
|
||||
{override.properties.map((p, j) => {
|
||||
const reg = p.custom ? customPropertiesRegistry : standardFieldConfigEditorRegistry;
|
||||
const item = reg?.getIfExists(p.prop);
|
||||
const item = registry.getIfExists(p.id);
|
||||
|
||||
if (!item) {
|
||||
return <div>Unknown property: {p.prop}</div>;
|
||||
return <div>Unknown property: {p.id}</div>;
|
||||
}
|
||||
|
||||
return (
|
||||
<div key={`${p.prop}/${j}`}>
|
||||
<div key={`${p.id}/${j}`}>
|
||||
<DynamicConfigValueEditor
|
||||
onChange={value => onDynamicConfigValueChange(j, value)}
|
||||
onRemove={() => onDynamicConfigValueRemove(j)}
|
||||
property={p}
|
||||
editorsRegistry={reg}
|
||||
registry={registry}
|
||||
context={{
|
||||
data,
|
||||
getSuggestions: (scope?: VariableSuggestionsScope) => getDataLinksVariableSuggestions(data, scope),
|
||||
|
||||
@@ -22,7 +22,7 @@ export const PanelOptionsEditor: React.FC<PanelOptionsEditorProps<any>> = ({ plu
|
||||
{optionEditors.list().map(e => {
|
||||
return (
|
||||
<Forms.Field label={e.name} description={e.description} key={e.id}>
|
||||
<e.editor value={lodashGet(options, e.id)} onChange={value => onOptionChange(e.id, value)} item={e} />
|
||||
<e.editor value={lodashGet(options, e.path)} onChange={value => onOptionChange(e.path, value)} item={e} />
|
||||
</Forms.Field>
|
||||
);
|
||||
})}
|
||||
|
||||
@@ -1,10 +1,46 @@
|
||||
import { PanelModel } from './PanelModel';
|
||||
import { getPanelPlugin } from '../../plugins/__mocks__/pluginMocks';
|
||||
import { PanelProps, FieldConfigProperty } from '@grafana/data';
|
||||
import {
|
||||
FieldConfigProperty,
|
||||
identityOverrideProcessor,
|
||||
PanelProps,
|
||||
standardFieldConfigEditorRegistry,
|
||||
} from '@grafana/data';
|
||||
import { ComponentClass } from 'react';
|
||||
|
||||
class TablePanelCtrl {}
|
||||
|
||||
export const mockStandardProperties = () => {
|
||||
const unit = {
|
||||
id: 'unit',
|
||||
path: 'unit',
|
||||
name: 'Unit',
|
||||
description: 'Value units',
|
||||
// @ts-ignore
|
||||
editor: () => null,
|
||||
// @ts-ignore
|
||||
override: () => null,
|
||||
process: identityOverrideProcessor,
|
||||
shouldApply: () => true,
|
||||
};
|
||||
|
||||
const decimals = {
|
||||
id: 'decimals',
|
||||
path: 'decimals',
|
||||
name: 'Decimals',
|
||||
description: 'Number of decimal to be shown for a value',
|
||||
// @ts-ignore
|
||||
editor: () => null,
|
||||
// @ts-ignore
|
||||
override: () => null,
|
||||
process: identityOverrideProcessor,
|
||||
shouldApply: () => true,
|
||||
};
|
||||
|
||||
return [unit, decimals];
|
||||
};
|
||||
standardFieldConfigEditorRegistry.setInit(() => mockStandardProperties());
|
||||
|
||||
describe('PanelModel', () => {
|
||||
describe('when creating new panel model', () => {
|
||||
let model: any;
|
||||
@@ -79,9 +115,16 @@ describe('PanelModel', () => {
|
||||
TablePanelCtrl // angular
|
||||
);
|
||||
panelPlugin.setDefaults(defaultOptionsMock);
|
||||
panelPlugin.useStandardFieldConfig([FieldConfigProperty.Unit, FieldConfigProperty.Decimals], {
|
||||
[FieldConfigProperty.Unit]: 'flop',
|
||||
[FieldConfigProperty.Decimals]: 2,
|
||||
/* panelPlugin.useStandardFieldConfig([FieldConfigOptionId.Unit, FieldConfigOptionId.Decimals], {
|
||||
[FieldConfigOptionId.Unit]: 'flop',
|
||||
[FieldConfigOptionId.Decimals]: 2,
|
||||
}); */
|
||||
panelPlugin.useFieldConfig({
|
||||
standardOptions: [FieldConfigProperty.Unit, FieldConfigProperty.Decimals],
|
||||
standardOptionsDefaults: {
|
||||
[FieldConfigProperty.Unit]: 'flop',
|
||||
[FieldConfigProperty.Decimals]: 2,
|
||||
},
|
||||
});
|
||||
model.pluginLoaded(panelPlugin);
|
||||
});
|
||||
@@ -100,9 +143,9 @@ describe('PanelModel', () => {
|
||||
|
||||
it('should apply field config defaults', () => {
|
||||
// default unit is overriden by model
|
||||
expect(model.getFieldOverrideOptions().fieldOptions.defaults.unit).toBe('mpg');
|
||||
expect(model.getFieldOverrideOptions().fieldConfig.defaults.unit).toBe('mpg');
|
||||
// default decimals are aplied
|
||||
expect(model.getFieldOverrideOptions().fieldOptions.defaults.decimals).toBe(2);
|
||||
expect(model.getFieldOverrideOptions().fieldConfig.defaults.decimals).toBe(2);
|
||||
});
|
||||
|
||||
it('should set model props on instance', () => {
|
||||
|
||||
@@ -415,9 +415,9 @@ export class PanelModel implements DataConfigSource {
|
||||
}
|
||||
|
||||
return {
|
||||
fieldOptions: this.fieldConfig,
|
||||
fieldConfig: this.fieldConfig,
|
||||
replaceVariables: this.replaceVariables,
|
||||
custom: this.plugin.customFieldConfigs,
|
||||
fieldConfigRegistry: this.plugin.fieldConfigRegistry,
|
||||
theme: config.theme,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -210,7 +210,7 @@ describe('PanelQueryRunner', () => {
|
||||
},
|
||||
{
|
||||
getFieldOverrideOptions: () => ({
|
||||
fieldOptions: {
|
||||
fieldConfig: {
|
||||
defaults: {
|
||||
unit: 'm/s',
|
||||
},
|
||||
|
||||
@@ -21,7 +21,7 @@ describe('getFieldDisplayValuesProxy', () => {
|
||||
],
|
||||
}),
|
||||
],
|
||||
fieldOptions: {
|
||||
fieldConfig: {
|
||||
defaults: {},
|
||||
overrides: [],
|
||||
},
|
||||
|
||||
@@ -129,7 +129,7 @@ describe('getLinksFromLogsField', () => {
|
||||
],
|
||||
}),
|
||||
],
|
||||
fieldOptions: {
|
||||
fieldConfig: {
|
||||
defaults: {},
|
||||
overrides: [],
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user