mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
FieldOverrides: Move FieldConfigSource from fieldOptions to PanelModel.fieldConfig (#22600)
* Apply field overrides in PanelChrome * Move applyFieldOverrides to panel query runner * Review updates * Make sure overrides are applied back on souce panel when exiting the new edit mode * TS ignores in est * Make field display work in viz repeater * Review updates * Review and test updates * Change the way overrides and trransformations are retrieved in PQR * Add fieldConfig property to PanelModel * Dashboard migration v1 * Use field config when exiting new panel edit mode * Gauge - use fieldConfig from panel model * FieldDisplayOptions - don's extend FieldConfigSource * Fix fieldDisplay ts * StatPanel updated * Stat panel defaults applied * Table2 panel options update * React graph updates * BarGauge updated * PieChart, Gauge, BarGauge and Stat updates * PieChart - remove field config defaults from options * FieldDisplayEditor - remove unused methos * PanelModel - remove debugger * Remove fieldConfig from field options when migrating dashboard * Update data links migrations * Update fieldDisaplay tests to respect new fieldConfig * Update dashboard schema version in snapshots * Fix BarGaugePanel test * Rebase fixes * Add onFieldConfigChange to PanelProps type * Update shared single stat migration * Pass PanelModel instead of options only for panel type change handler [breaking] * Renames * Don't mutate panel options * Migrations update * Remove obsolete snap * Minor updates after review * Fix null checks * Temporarily (until we decide to switch to new pane edit) bring back old aditors * Temporarily rename ValueMappingEditor and MappingRow to Legacy* * Migrations update * Updae setFieldConfigDefaults API * Update the way field config defaults are applied * Use standard field config for gauge, bar gauge and stat panels * refactoring * Revert dashboard fieldOptions migrations as those are handled by single stat migrator * Fix ts in tests * Strict null fix and some minor fixes Co-authored-by: Torkel Ödegaard <torkel@grafana.com>
This commit is contained in:
@@ -89,33 +89,25 @@ export class PanelEditorUnconnected extends PureComponent<Props> {
|
||||
this.props.updateLocation({ query: { tab: tab.id }, partial: true });
|
||||
};
|
||||
|
||||
onFieldConfigsChange = (fieldOptions: FieldConfigSource) => {
|
||||
// NOTE: for now, assume this is from 'fieldOptions' -- TODO? put on panel model directly?
|
||||
onFieldConfigChange = (config: FieldConfigSource) => {
|
||||
const { panel } = this.props;
|
||||
const options = panel.getOptions();
|
||||
panel.updateOptions({
|
||||
...options,
|
||||
fieldOptions, // Assume it is from shared singlestat -- TODO own property?
|
||||
|
||||
panel.updateFieldConfig({
|
||||
...config,
|
||||
});
|
||||
this.forceUpdate();
|
||||
};
|
||||
|
||||
renderFieldOptions(plugin: PanelPlugin) {
|
||||
const { panel, data } = this.props;
|
||||
const { fieldConfig } = panel;
|
||||
|
||||
const fieldOptions = panel.options['fieldOptions'] as FieldConfigSource;
|
||||
|
||||
if (!fieldOptions) {
|
||||
if (!fieldConfig) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<FieldConfigEditor
|
||||
config={fieldOptions}
|
||||
plugin={plugin}
|
||||
onChange={this.onFieldConfigsChange}
|
||||
data={data.series}
|
||||
/>
|
||||
<FieldConfigEditor config={fieldConfig} plugin={plugin} onChange={this.onFieldConfigChange} data={data.series} />
|
||||
);
|
||||
}
|
||||
|
||||
@@ -130,7 +122,13 @@ export class PanelEditorUnconnected extends PureComponent<Props> {
|
||||
if (plugin.editor && panel) {
|
||||
return (
|
||||
<div style={{ marginTop: '10px' }}>
|
||||
<plugin.editor data={data} options={panel.getOptions()} onOptionsChange={this.onPanelOptionsChanged} />
|
||||
<plugin.editor
|
||||
data={data}
|
||||
options={panel.getOptions()}
|
||||
onOptionsChange={this.onPanelOptionsChanged}
|
||||
fieldConfig={panel.getFieldConfig()}
|
||||
onFieldConfigChange={this.onFieldConfigChange}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -89,7 +89,7 @@ describe('ShareModal', () => {
|
||||
},
|
||||
};
|
||||
ctx.mount({
|
||||
panel: { id: 22, options: {} },
|
||||
panel: { id: 22, options: {}, fieldConfig: { defaults: {}, overrides: [] } },
|
||||
});
|
||||
});
|
||||
|
||||
@@ -101,7 +101,7 @@ describe('ShareModal', () => {
|
||||
it('should generate render url', () => {
|
||||
mockLocationHref('http://dashboards.grafana.com/d/abcdefghi/my-dash');
|
||||
ctx.mount({
|
||||
panel: { id: 22, options: {} },
|
||||
panel: { id: 22, options: {}, fieldConfig: { defaults: {}, overrides: [] } },
|
||||
});
|
||||
|
||||
const state = ctx.wrapper?.state();
|
||||
@@ -113,7 +113,7 @@ describe('ShareModal', () => {
|
||||
it('should generate render url for scripted dashboard', () => {
|
||||
mockLocationHref('http://dashboards.grafana.com/dashboard/script/my-dash.js');
|
||||
ctx.mount({
|
||||
panel: { id: 22, options: {} },
|
||||
panel: { id: 22, options: {}, fieldConfig: { defaults: {}, overrides: [] } },
|
||||
});
|
||||
|
||||
const state = ctx.wrapper?.state();
|
||||
@@ -142,7 +142,7 @@ describe('ShareModal', () => {
|
||||
it('should remove fullscreen from image url when is first param in querystring and modeSharePanel is true', () => {
|
||||
mockLocationHref('http://server/#!/test?fullscreen&edit');
|
||||
ctx.mount({
|
||||
panel: { id: 1, options: {} },
|
||||
panel: { id: 1, options: {}, fieldConfig: { defaults: {}, overrides: [] } },
|
||||
});
|
||||
|
||||
const state = ctx.wrapper?.state();
|
||||
@@ -153,7 +153,7 @@ describe('ShareModal', () => {
|
||||
it('should remove edit from image url when is first param in querystring and modeSharePanel is true', () => {
|
||||
mockLocationHref('http://server/#!/test?edit&fullscreen');
|
||||
ctx.mount({
|
||||
panel: { id: 1, options: {} },
|
||||
panel: { id: 1, options: {}, fieldConfig: { defaults: {}, overrides: [] } },
|
||||
});
|
||||
|
||||
const state = ctx.wrapper?.state();
|
||||
|
||||
@@ -24,6 +24,7 @@ import {
|
||||
PanelEvents,
|
||||
PanelData,
|
||||
PanelPlugin,
|
||||
FieldConfigSource,
|
||||
} from '@grafana/data';
|
||||
|
||||
const DEFAULT_PLUGIN_ERROR = 'Error in plugin';
|
||||
@@ -217,6 +218,10 @@ export class PanelChrome extends PureComponent<Props, State> {
|
||||
this.props.panel.updateOptions(options);
|
||||
};
|
||||
|
||||
onFieldConfigChange = (config: FieldConfigSource) => {
|
||||
this.props.panel.updateFieldConfig(config);
|
||||
};
|
||||
|
||||
onPanelError = (message: string) => {
|
||||
if (this.state.errorMessage !== message) {
|
||||
this.setState({ errorMessage: message });
|
||||
@@ -281,12 +286,14 @@ export class PanelChrome extends PureComponent<Props, State> {
|
||||
timeRange={timeRange}
|
||||
timeZone={this.props.dashboard.getTimezone()}
|
||||
options={panelOptions}
|
||||
fieldConfig={panel.fieldConfig}
|
||||
transparent={panel.transparent}
|
||||
width={panelWidth}
|
||||
height={innerPanelHeight}
|
||||
renderCounter={renderCounter}
|
||||
replaceVariables={panel.replaceVariables}
|
||||
onOptionsChange={this.onOptionsChange}
|
||||
onFieldConfigChange={this.onFieldConfigChange}
|
||||
onChangeTimeRange={this.onChangeTimeRange}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -15,7 +15,14 @@ import { PanelModel, DashboardModel } from '../state';
|
||||
import { VizPickerSearch } from './VizPickerSearch';
|
||||
import PluginStateinfo from 'app/features/plugins/PluginStateInfo';
|
||||
import { Unsubscribable } from 'rxjs';
|
||||
import { PanelPlugin, PanelPluginMeta, PanelData, LoadingState, DefaultTimeRange } from '@grafana/data';
|
||||
import {
|
||||
PanelPlugin,
|
||||
PanelPluginMeta,
|
||||
PanelData,
|
||||
LoadingState,
|
||||
DefaultTimeRange,
|
||||
FieldConfigSource,
|
||||
} from '@grafana/data';
|
||||
|
||||
interface Props {
|
||||
panel: PanelModel;
|
||||
@@ -59,6 +66,11 @@ export class VisualizationTab extends PureComponent<Props, State> {
|
||||
return panel.getOptions();
|
||||
};
|
||||
|
||||
getReactPanelFieldConfig = () => {
|
||||
const { panel } = this.props;
|
||||
return panel.getFieldConfig();
|
||||
};
|
||||
|
||||
renderPanelOptions() {
|
||||
const { plugin, dashboard, panel } = this.props;
|
||||
|
||||
@@ -72,6 +84,10 @@ export class VisualizationTab extends PureComponent<Props, State> {
|
||||
data={this.state.data}
|
||||
options={this.getReactPanelOptions()}
|
||||
onOptionsChange={this.onPanelOptionsChanged}
|
||||
// TODO[FieldConfig]: Remove when we switch old editor to new
|
||||
fieldConfig={this.getReactPanelFieldConfig()}
|
||||
// TODO[FieldConfig]: Remove when we switch old editor to new
|
||||
onFieldConfigChange={this.onPanelFieldConfigChange}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -103,6 +119,12 @@ export class VisualizationTab extends PureComponent<Props, State> {
|
||||
this.forceUpdate(callback);
|
||||
};
|
||||
|
||||
// TODO[FieldConfig]: Remove when we switch old editor to new
|
||||
onPanelFieldConfigChange = (config: FieldConfigSource, callback?: () => void) => {
|
||||
this.props.panel.updateFieldConfig(config);
|
||||
this.forceUpdate(callback);
|
||||
};
|
||||
|
||||
onOpenVizPicker = () => {
|
||||
this.setState({ isVizPickerOpen: true, scrollTop: 0 });
|
||||
};
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { PanelModel } from './PanelModel';
|
||||
import { getPanelPlugin } from '../../plugins/__mocks__/pluginMocks';
|
||||
import { PanelProps } from '@grafana/data';
|
||||
import { ConfigOverrideRule, PanelProps } from '@grafana/data';
|
||||
import { ComponentClass } from 'react';
|
||||
|
||||
class TablePanelCtrl {}
|
||||
@@ -53,9 +53,31 @@ describe('PanelModel', () => {
|
||||
showColumns: true,
|
||||
targets: [{ refId: 'A' }, { noRefId: true }],
|
||||
options: persistedOptionsMock,
|
||||
fieldConfig: {
|
||||
defaults: {
|
||||
unit: 'mpg',
|
||||
},
|
||||
overrides: [
|
||||
{
|
||||
matcher: {
|
||||
id: '1',
|
||||
options: {},
|
||||
},
|
||||
properties: [],
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
model = new PanelModel(modelJson);
|
||||
const overrideMock: ConfigOverrideRule = {
|
||||
matcher: {
|
||||
id: '2',
|
||||
options: {},
|
||||
},
|
||||
properties: [],
|
||||
};
|
||||
|
||||
const panelPlugin = getPanelPlugin(
|
||||
{
|
||||
id: 'table',
|
||||
@@ -64,6 +86,13 @@ describe('PanelModel', () => {
|
||||
TablePanelCtrl // angular
|
||||
);
|
||||
panelPlugin.setDefaults(defaultOptionsMock);
|
||||
panelPlugin.setFieldConfigDefaults({
|
||||
defaults: {
|
||||
unit: 'flop',
|
||||
decimals: 2,
|
||||
},
|
||||
overrides: [overrideMock],
|
||||
});
|
||||
model.pluginLoaded(panelPlugin);
|
||||
});
|
||||
|
||||
@@ -79,6 +108,17 @@ describe('PanelModel', () => {
|
||||
expect(model.getOptions().arrayWith2Values.length).toBe(1);
|
||||
});
|
||||
|
||||
it('should merge override field config options', () => {
|
||||
expect(model.getFieldOverrideOptions().fieldOptions.overrides.length).toBe(2);
|
||||
});
|
||||
|
||||
it('should apply field config defaults', () => {
|
||||
// default unit is overriden by model
|
||||
expect(model.getFieldOverrideOptions().fieldOptions.defaults.unit).toBe('mpg');
|
||||
// default decimals are aplied
|
||||
expect(model.getFieldOverrideOptions().fieldOptions.defaults.decimals).toBe(2);
|
||||
});
|
||||
|
||||
it('should set model props on instance', () => {
|
||||
expect(model.showColumns).toBe(true);
|
||||
});
|
||||
|
||||
@@ -15,6 +15,7 @@ import {
|
||||
PanelEvents,
|
||||
PanelPlugin,
|
||||
ScopedVars,
|
||||
FieldConfigSource,
|
||||
} from '@grafana/data';
|
||||
import { EDIT_PANEL_ID } from 'app/core/constants';
|
||||
|
||||
@@ -81,6 +82,7 @@ const mustKeepProps: { [str: string]: boolean } = {
|
||||
pluginVersion: true,
|
||||
queryRunner: true,
|
||||
transformations: true,
|
||||
fieldConfig: true,
|
||||
};
|
||||
|
||||
const defaults: any = {
|
||||
@@ -121,6 +123,7 @@ export class PanelModel implements DataConfigSource {
|
||||
options: {
|
||||
[key: string]: any;
|
||||
};
|
||||
fieldConfig: FieldConfigSource;
|
||||
|
||||
maxDataPoints?: number;
|
||||
interval?: string;
|
||||
@@ -177,9 +180,19 @@ export class PanelModel implements DataConfigSource {
|
||||
getOptions() {
|
||||
return this.options;
|
||||
}
|
||||
getFieldConfig() {
|
||||
return this.fieldConfig;
|
||||
}
|
||||
|
||||
updateOptions(options: object) {
|
||||
this.options = options;
|
||||
|
||||
this.render();
|
||||
}
|
||||
|
||||
updateFieldConfig(config: FieldConfigSource) {
|
||||
this.fieldConfig = config;
|
||||
|
||||
this.resendLastResult();
|
||||
this.render();
|
||||
}
|
||||
@@ -273,6 +286,23 @@ export class PanelModel implements DataConfigSource {
|
||||
return srcValue;
|
||||
}
|
||||
});
|
||||
|
||||
this.fieldConfig = {
|
||||
defaults: _.mergeWith(
|
||||
{},
|
||||
plugin.fieldConfigDefaults.defaults,
|
||||
this.fieldConfig ? this.fieldConfig.defaults : {},
|
||||
(objValue: any, srcValue: any): any => {
|
||||
if (_.isArray(srcValue)) {
|
||||
return srcValue;
|
||||
}
|
||||
}
|
||||
),
|
||||
overrides: [
|
||||
...plugin.fieldConfigDefaults.overrides,
|
||||
...(this.fieldConfig && this.fieldConfig.overrides ? this.fieldConfig.overrides : []),
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
pluginLoaded(plugin: PanelPlugin) {
|
||||
@@ -382,7 +412,7 @@ export class PanelModel implements DataConfigSource {
|
||||
}
|
||||
|
||||
return {
|
||||
fieldOptions: this.options.fieldOptions,
|
||||
fieldOptions: this.fieldConfig,
|
||||
replaceVariables: this.replaceVariables,
|
||||
custom: this.plugin.customFieldConfigs,
|
||||
theme: config.theme,
|
||||
|
||||
@@ -43,8 +43,43 @@ describe('BarGauge Panel Migrations', () => {
|
||||
targets: [],
|
||||
title: 'Usage',
|
||||
type: 'bargauge',
|
||||
} as PanelModel;
|
||||
} as Omit<PanelModel, 'fieldConfig'>;
|
||||
|
||||
expect(barGaugePanelMigrationHandler(panel)).toMatchSnapshot();
|
||||
expect(barGaugePanelMigrationHandler(panel as PanelModel)).toMatchSnapshot();
|
||||
expect((panel as any).fieldConfig).toMatchInlineSnapshot(`
|
||||
Object {
|
||||
"defaults": Object {
|
||||
"color": Object {
|
||||
"mode": "thresholds",
|
||||
},
|
||||
"decimals": null,
|
||||
"mappings": Array [],
|
||||
"max": 33,
|
||||
"min": -22,
|
||||
"thresholds": Object {
|
||||
"mode": "absolute",
|
||||
"steps": Array [
|
||||
Object {
|
||||
"color": "green",
|
||||
"index": 0,
|
||||
"value": -Infinity,
|
||||
},
|
||||
Object {
|
||||
"color": "orange",
|
||||
"index": 1,
|
||||
"value": 40,
|
||||
},
|
||||
Object {
|
||||
"color": "red",
|
||||
"index": 2,
|
||||
"value": 80,
|
||||
},
|
||||
],
|
||||
},
|
||||
"unit": "watt",
|
||||
},
|
||||
"overrides": Array [],
|
||||
}
|
||||
`);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -8,6 +8,7 @@ import {
|
||||
PanelProps,
|
||||
LoadingState,
|
||||
dateTime,
|
||||
FieldConfigSource,
|
||||
toDataFrame,
|
||||
} from '@grafana/data';
|
||||
import { BarGaugeDisplayMode } from '@grafana/ui';
|
||||
@@ -66,13 +67,15 @@ function createBarGaugePanelWithData(data: PanelData): ReactWrapper<PanelProps<B
|
||||
displayMode: BarGaugeDisplayMode.Lcd,
|
||||
fieldOptions: {
|
||||
calcs: ['mean'],
|
||||
defaults: {},
|
||||
values: false,
|
||||
overrides: [],
|
||||
},
|
||||
orientation: VizOrientation.Horizontal,
|
||||
showUnfilled: true,
|
||||
};
|
||||
const fieldConfig: FieldConfigSource = {
|
||||
defaults: {},
|
||||
overrides: [],
|
||||
};
|
||||
|
||||
return mount<BarGaugePanel>(
|
||||
<BarGaugePanel
|
||||
@@ -81,6 +84,8 @@ function createBarGaugePanelWithData(data: PanelData): ReactWrapper<PanelProps<B
|
||||
timeRange={timeRange}
|
||||
timeZone={'utc'}
|
||||
options={options}
|
||||
fieldConfig={fieldConfig}
|
||||
onFieldConfigChange={() => {}}
|
||||
onOptionsChange={() => {}}
|
||||
onChangeTimeRange={() => {}}
|
||||
replaceVariables={s => s}
|
||||
|
||||
@@ -51,9 +51,10 @@ export class BarGaugePanel extends PureComponent<PanelProps<BarGaugeOptions>> {
|
||||
};
|
||||
|
||||
getValues = (): FieldDisplay[] => {
|
||||
const { data, options, replaceVariables } = this.props;
|
||||
const { data, options, replaceVariables, fieldConfig } = this.props;
|
||||
return getFieldDisplayValues({
|
||||
...options,
|
||||
fieldConfig,
|
||||
fieldOptions: options.fieldOptions,
|
||||
replaceVariables,
|
||||
theme: config.theme,
|
||||
data: data.series,
|
||||
|
||||
@@ -2,83 +2,94 @@
|
||||
import React, { PureComponent } from 'react';
|
||||
|
||||
import {
|
||||
ThresholdsEditor,
|
||||
ValueMappingsEditor,
|
||||
PanelOptionsGrid,
|
||||
FieldDisplayEditor,
|
||||
FieldPropertiesEditor,
|
||||
PanelOptionsGroup,
|
||||
FormLabel,
|
||||
Select,
|
||||
DataLinksEditor,
|
||||
Switch,
|
||||
FieldPropertiesEditor,
|
||||
ThresholdsEditor,
|
||||
LegacyValueMappingsEditor,
|
||||
DataLinksEditor,
|
||||
} from '@grafana/ui';
|
||||
import {
|
||||
DataLink,
|
||||
FieldConfig,
|
||||
FieldDisplayOptions,
|
||||
PanelEditorProps,
|
||||
ThresholdsConfig,
|
||||
ValueMapping,
|
||||
FieldDisplayOptions,
|
||||
FieldConfig,
|
||||
DataLink,
|
||||
PanelEditorProps,
|
||||
} from '@grafana/data';
|
||||
import { BarGaugeOptions, displayModes } from './types';
|
||||
import { orientationOptions } from '../gauge/types';
|
||||
import {
|
||||
getDataLinksVariableSuggestions,
|
||||
getCalculationValueDataLinksVariableSuggestions,
|
||||
} from 'app/features/panel/panellinks/link_srv';
|
||||
getDataLinksVariableSuggestions,
|
||||
} from '../../../features/panel/panellinks/link_srv';
|
||||
|
||||
export class BarGaugePanelEditor extends PureComponent<PanelEditorProps<BarGaugeOptions>> {
|
||||
onThresholdsChanged = (thresholds: ThresholdsConfig) => {
|
||||
const current = this.props.options.fieldOptions.defaults;
|
||||
this.onDefaultsChange({
|
||||
...current,
|
||||
thresholds,
|
||||
});
|
||||
};
|
||||
|
||||
onValueMappingsChanged = (mappings: ValueMapping[]) => {
|
||||
const current = this.props.options.fieldOptions.defaults;
|
||||
this.onDefaultsChange({
|
||||
...current,
|
||||
mappings,
|
||||
});
|
||||
};
|
||||
|
||||
onDisplayOptionsChanged = (fieldOptions: FieldDisplayOptions) =>
|
||||
this.props.onOptionsChange({
|
||||
...this.props.options,
|
||||
fieldOptions,
|
||||
});
|
||||
|
||||
onDefaultsChange = (field: FieldConfig) => {
|
||||
this.onDisplayOptionsChanged({
|
||||
...this.props.options.fieldOptions,
|
||||
defaults: field,
|
||||
});
|
||||
};
|
||||
|
||||
onOrientationChange = ({ value }: any) => this.props.onOptionsChange({ ...this.props.options, orientation: value });
|
||||
onDisplayModeChange = ({ value }: any) => this.props.onOptionsChange({ ...this.props.options, displayMode: value });
|
||||
onToggleShowUnfilled = () => {
|
||||
this.props.onOptionsChange({ ...this.props.options, showUnfilled: !this.props.options.showUnfilled });
|
||||
};
|
||||
|
||||
onDataLinksChanged = (links: DataLink[]) => {
|
||||
this.onDefaultsChange({
|
||||
...this.props.options.fieldOptions.defaults,
|
||||
links,
|
||||
onThresholdsChanged = (thresholds: ThresholdsConfig) => {
|
||||
const current = this.props.fieldConfig;
|
||||
this.props.onFieldConfigChange({
|
||||
...current,
|
||||
defaults: {
|
||||
...current.defaults,
|
||||
thresholds,
|
||||
},
|
||||
});
|
||||
};
|
||||
render() {
|
||||
const { options } = this.props;
|
||||
const { fieldOptions } = options;
|
||||
const { defaults } = fieldOptions;
|
||||
|
||||
onValueMappingsChanged = (mappings: ValueMapping[]) => {
|
||||
const current = this.props.fieldConfig;
|
||||
this.props.onFieldConfigChange({
|
||||
...current,
|
||||
defaults: {
|
||||
...current.defaults,
|
||||
mappings,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
onDataLinksChanged = (links: DataLink[]) => {
|
||||
const current = this.props.fieldConfig;
|
||||
this.props.onFieldConfigChange({
|
||||
...current,
|
||||
defaults: {
|
||||
...current.defaults,
|
||||
links,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
onDefaultsChange = (field: FieldConfig, event?: React.SyntheticEvent<HTMLElement>, callback?: () => void) => {
|
||||
this.props.onFieldConfigChange({
|
||||
...this.props.fieldConfig,
|
||||
defaults: field,
|
||||
});
|
||||
};
|
||||
|
||||
render() {
|
||||
const { options, fieldConfig } = this.props;
|
||||
const { fieldOptions } = options;
|
||||
const { defaults } = fieldConfig;
|
||||
|
||||
const labelWidth = 6;
|
||||
const suggestions = fieldOptions.values
|
||||
? getDataLinksVariableSuggestions(this.props.data.series)
|
||||
: getCalculationValueDataLinksVariableSuggestions(this.props.data.series);
|
||||
const labelWidth = 6;
|
||||
|
||||
return (
|
||||
<>
|
||||
@@ -105,14 +116,16 @@ export class BarGaugePanelEditor extends PureComponent<PanelEditorProps<BarGauge
|
||||
value={displayModes.find(item => item.value === options.displayMode)}
|
||||
/>
|
||||
</div>
|
||||
{options.displayMode !== 'lcd' && (
|
||||
<Switch
|
||||
label="Unfilled"
|
||||
labelClass={`width-${labelWidth}`}
|
||||
checked={options.showUnfilled}
|
||||
onChange={this.onToggleShowUnfilled}
|
||||
/>
|
||||
)}
|
||||
<>
|
||||
{options.displayMode !== 'lcd' && (
|
||||
<Switch
|
||||
label="Unfilled"
|
||||
labelClass={`width-${labelWidth}`}
|
||||
checked={options.showUnfilled}
|
||||
onChange={this.onToggleShowUnfilled}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
</PanelOptionsGroup>
|
||||
<PanelOptionsGroup title="Field">
|
||||
<FieldPropertiesEditor
|
||||
@@ -126,7 +139,7 @@ export class BarGaugePanelEditor extends PureComponent<PanelEditorProps<BarGauge
|
||||
<ThresholdsEditor onChange={this.onThresholdsChanged} thresholds={defaults.thresholds} />
|
||||
</PanelOptionsGrid>
|
||||
|
||||
<ValueMappingsEditor onChange={this.onValueMappingsChanged} valueMappings={defaults.mappings} />
|
||||
<LegacyValueMappingsEditor onChange={this.onValueMappingsChanged} valueMappings={defaults.mappings} />
|
||||
|
||||
<PanelOptionsGroup title="Data links">
|
||||
<DataLinksEditor
|
||||
|
||||
@@ -7,37 +7,6 @@ Object {
|
||||
"calcs": Array [
|
||||
"mean",
|
||||
],
|
||||
"defaults": Object {
|
||||
"color": Object {
|
||||
"mode": "thresholds",
|
||||
},
|
||||
"decimals": null,
|
||||
"mappings": Array [],
|
||||
"max": 33,
|
||||
"min": -22,
|
||||
"thresholds": Object {
|
||||
"mode": "absolute",
|
||||
"steps": Array [
|
||||
Object {
|
||||
"color": "green",
|
||||
"index": 0,
|
||||
"value": -Infinity,
|
||||
},
|
||||
Object {
|
||||
"color": "orange",
|
||||
"index": 1,
|
||||
"value": 40,
|
||||
},
|
||||
Object {
|
||||
"color": "red",
|
||||
"index": 2,
|
||||
"value": 80,
|
||||
},
|
||||
],
|
||||
},
|
||||
"unit": "watt",
|
||||
},
|
||||
"overrides": Array [],
|
||||
"thresholds": Array [
|
||||
Object {
|
||||
"color": "green",
|
||||
|
||||
@@ -3,10 +3,12 @@ import { PanelPlugin } from '@grafana/data';
|
||||
import { BarGaugePanel } from './BarGaugePanel';
|
||||
import { BarGaugePanelEditor } from './BarGaugePanelEditor';
|
||||
import { BarGaugeOptions, defaults } from './types';
|
||||
import { standardFieldConfig } from '../stat/types';
|
||||
import { barGaugePanelMigrationHandler } from './BarGaugeMigrations';
|
||||
|
||||
export const plugin = new PanelPlugin<BarGaugeOptions>(BarGaugePanel)
|
||||
.setDefaults(defaults)
|
||||
.setFieldConfigDefaults(standardFieldConfig)
|
||||
.setEditor(BarGaugePanelEditor)
|
||||
.setPanelChangeHandler(sharedSingleStatPanelChangedHandler)
|
||||
.setMigrationHandler(barGaugePanelMigrationHandler);
|
||||
|
||||
@@ -75,9 +75,68 @@ describe('Gauge Panel Migrations', () => {
|
||||
timeShift: null,
|
||||
title: 'Panel Title',
|
||||
type: 'gauge',
|
||||
} as PanelModel;
|
||||
} as Omit<PanelModel, 'fieldConfig'>;
|
||||
|
||||
expect(gaugePanelMigrationHandler(panel)).toMatchSnapshot();
|
||||
const result = gaugePanelMigrationHandler(panel as PanelModel);
|
||||
expect(result).toMatchSnapshot();
|
||||
|
||||
// Ignored due to the API change
|
||||
//@ts-ignore
|
||||
expect(result.fieldOptions.defaults).toBeUndefined();
|
||||
// Ignored due to the API change
|
||||
//@ts-ignore
|
||||
expect(result.fieldOptions.overrides).toBeUndefined();
|
||||
|
||||
expect((panel as PanelModel).fieldConfig).toMatchInlineSnapshot(`
|
||||
Object {
|
||||
"defaults": Object {
|
||||
"color": Object {
|
||||
"mode": "thresholds",
|
||||
},
|
||||
"decimals": 3,
|
||||
"mappings": Array [
|
||||
Object {
|
||||
"from": "50",
|
||||
"id": 1,
|
||||
"operator": "",
|
||||
"text": "BIG",
|
||||
"to": "1000",
|
||||
"type": 2,
|
||||
"value": "",
|
||||
},
|
||||
],
|
||||
"max": "50",
|
||||
"min": "-50",
|
||||
"thresholds": Object {
|
||||
"mode": "absolute",
|
||||
"steps": Array [
|
||||
Object {
|
||||
"color": "green",
|
||||
"index": 0,
|
||||
"value": -Infinity,
|
||||
},
|
||||
Object {
|
||||
"color": "#EAB839",
|
||||
"index": 1,
|
||||
"value": -25,
|
||||
},
|
||||
Object {
|
||||
"color": "#6ED0E0",
|
||||
"index": 2,
|
||||
"value": 0,
|
||||
},
|
||||
Object {
|
||||
"color": "red",
|
||||
"index": 3,
|
||||
"value": 25,
|
||||
},
|
||||
],
|
||||
},
|
||||
"unit": "accMS2",
|
||||
},
|
||||
"overrides": Array [],
|
||||
}
|
||||
`);
|
||||
});
|
||||
|
||||
it('change from angular singlestat to gauge', () => {
|
||||
@@ -95,11 +154,12 @@ describe('Gauge Panel Migrations', () => {
|
||||
},
|
||||
};
|
||||
|
||||
const newOptions = gaugePanelChangedHandler({} as any, 'singlestat', old);
|
||||
expect(newOptions.fieldOptions.defaults.unit).toBe('ms');
|
||||
expect(newOptions.fieldOptions.defaults.min).toBe(-10);
|
||||
expect(newOptions.fieldOptions.defaults.max).toBe(150);
|
||||
expect(newOptions.fieldOptions.defaults.decimals).toBe(7);
|
||||
const panel = {} as PanelModel;
|
||||
const newOptions = gaugePanelChangedHandler(panel, 'singlestat', old);
|
||||
expect(panel.fieldConfig.defaults.unit).toBe('ms');
|
||||
expect(panel.fieldConfig.defaults.min).toBe(-10);
|
||||
expect(panel.fieldConfig.defaults.max).toBe(150);
|
||||
expect(panel.fieldConfig.defaults.decimals).toBe(7);
|
||||
expect(newOptions.showThresholdMarkers).toBe(true);
|
||||
expect(newOptions.showThresholdLabels).toBe(true);
|
||||
});
|
||||
@@ -116,10 +176,10 @@ describe('Gauge Panel Migrations', () => {
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const newOptions = gaugePanelChangedHandler({} as any, 'singlestat', old);
|
||||
expect(newOptions.fieldOptions.defaults.unit).toBe('ms');
|
||||
expect(newOptions.fieldOptions.defaults.min).toBe(undefined);
|
||||
expect(newOptions.fieldOptions.defaults.max).toBe(undefined);
|
||||
const panel = {} as PanelModel;
|
||||
gaugePanelChangedHandler(panel, 'singlestat', old);
|
||||
expect(panel.fieldConfig.defaults.unit).toBe('ms');
|
||||
expect(panel.fieldConfig.defaults.min).toBe(undefined);
|
||||
expect(panel.fieldConfig.defaults.max).toBe(undefined);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -40,8 +40,9 @@ export class GaugePanel extends PureComponent<PanelProps<GaugeOptions>> {
|
||||
};
|
||||
|
||||
getValues = (): FieldDisplay[] => {
|
||||
const { data, options, replaceVariables } = this.props;
|
||||
const { data, options, replaceVariables, fieldConfig } = this.props;
|
||||
return getFieldDisplayValues({
|
||||
fieldConfig,
|
||||
fieldOptions: options.fieldOptions,
|
||||
replaceVariables,
|
||||
theme: config.theme,
|
||||
|
||||
@@ -1,29 +1,29 @@
|
||||
// Libraries
|
||||
import React, { PureComponent } from 'react';
|
||||
import {
|
||||
ThresholdsEditor,
|
||||
PanelOptionsGrid,
|
||||
ValueMappingsEditor,
|
||||
FieldDisplayEditor,
|
||||
FieldPropertiesEditor,
|
||||
Switch,
|
||||
PanelOptionsGroup,
|
||||
FieldPropertiesEditor,
|
||||
ThresholdsEditor,
|
||||
LegacyValueMappingsEditor,
|
||||
DataLinksEditor,
|
||||
} from '@grafana/ui';
|
||||
import {
|
||||
PanelEditorProps,
|
||||
FieldDisplayOptions,
|
||||
ThresholdsConfig,
|
||||
ValueMapping,
|
||||
FieldConfig,
|
||||
DataLink,
|
||||
FieldConfig,
|
||||
ValueMapping,
|
||||
} from '@grafana/data';
|
||||
|
||||
import { GaugeOptions } from './types';
|
||||
import {
|
||||
getCalculationValueDataLinksVariableSuggestions,
|
||||
getDataLinksVariableSuggestions,
|
||||
} from 'app/features/panel/panellinks/link_srv';
|
||||
} from '../../../features/panel/panellinks/link_srv';
|
||||
|
||||
export class GaugePanelEditor extends PureComponent<PanelEditorProps<GaugeOptions>> {
|
||||
labelWidth = 6;
|
||||
@@ -37,27 +37,11 @@ export class GaugePanelEditor extends PureComponent<PanelEditorProps<GaugeOption
|
||||
showThresholdMarkers: !this.props.options.showThresholdMarkers,
|
||||
});
|
||||
|
||||
onThresholdsChanged = (thresholds: ThresholdsConfig) => {
|
||||
const current = this.props.options.fieldOptions.defaults;
|
||||
this.onDefaultsChange({
|
||||
...current,
|
||||
thresholds,
|
||||
});
|
||||
};
|
||||
|
||||
onValueMappingsChanged = (mappings: ValueMapping[]) => {
|
||||
const current = this.props.options.fieldOptions.defaults;
|
||||
this.onDefaultsChange({
|
||||
...current,
|
||||
mappings,
|
||||
});
|
||||
};
|
||||
|
||||
onDisplayOptionsChanged = (
|
||||
fieldOptions: FieldDisplayOptions,
|
||||
event?: React.SyntheticEvent<HTMLElement>,
|
||||
callback?: () => void
|
||||
) =>
|
||||
) => {
|
||||
this.props.onOptionsChange(
|
||||
{
|
||||
...this.props.options,
|
||||
@@ -65,38 +49,57 @@ export class GaugePanelEditor extends PureComponent<PanelEditorProps<GaugeOption
|
||||
},
|
||||
callback
|
||||
);
|
||||
|
||||
onDefaultsChange = (field: FieldConfig, event?: React.SyntheticEvent<HTMLElement>, callback?: () => void) => {
|
||||
this.onDisplayOptionsChanged(
|
||||
{
|
||||
...this.props.options.fieldOptions,
|
||||
defaults: field,
|
||||
},
|
||||
event,
|
||||
callback
|
||||
);
|
||||
};
|
||||
|
||||
onDataLinksChanged = (links: DataLink[], callback?: () => void) => {
|
||||
this.onDefaultsChange(
|
||||
{
|
||||
...this.props.options.fieldOptions.defaults,
|
||||
onThresholdsChanged = (thresholds: ThresholdsConfig) => {
|
||||
const current = this.props.fieldConfig;
|
||||
this.props.onFieldConfigChange({
|
||||
...current,
|
||||
defaults: {
|
||||
...current.defaults,
|
||||
thresholds,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
onValueMappingsChanged = (mappings: ValueMapping[]) => {
|
||||
const current = this.props.fieldConfig;
|
||||
this.props.onFieldConfigChange({
|
||||
...current,
|
||||
defaults: {
|
||||
...current.defaults,
|
||||
mappings,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
onDataLinksChanged = (links: DataLink[]) => {
|
||||
const current = this.props.fieldConfig;
|
||||
this.props.onFieldConfigChange({
|
||||
...current,
|
||||
defaults: {
|
||||
...current.defaults,
|
||||
links,
|
||||
},
|
||||
undefined,
|
||||
callback
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
onDefaultsChange = (field: FieldConfig) => {
|
||||
this.props.onFieldConfigChange({
|
||||
...this.props.fieldConfig,
|
||||
defaults: field,
|
||||
});
|
||||
};
|
||||
|
||||
render() {
|
||||
const { options } = this.props;
|
||||
const { fieldOptions, showThresholdLabels, showThresholdMarkers } = options;
|
||||
const { defaults } = fieldOptions;
|
||||
const { options, fieldConfig } = this.props;
|
||||
const { showThresholdLabels, showThresholdMarkers, fieldOptions } = options;
|
||||
|
||||
const { defaults } = fieldConfig;
|
||||
|
||||
const suggestions = fieldOptions.values
|
||||
? getDataLinksVariableSuggestions(this.props.data.series)
|
||||
: getCalculationValueDataLinksVariableSuggestions(this.props.data.series);
|
||||
|
||||
return (
|
||||
<>
|
||||
<PanelOptionsGrid>
|
||||
@@ -128,11 +131,9 @@ export class GaugePanelEditor extends PureComponent<PanelEditorProps<GaugeOption
|
||||
value={defaults}
|
||||
/>
|
||||
</PanelOptionsGroup>
|
||||
|
||||
<ThresholdsEditor onChange={this.onThresholdsChanged} thresholds={defaults.thresholds} />
|
||||
</PanelOptionsGrid>
|
||||
|
||||
<ValueMappingsEditor onChange={this.onValueMappingsChanged} valueMappings={defaults.mappings} />
|
||||
<LegacyValueMappingsEditor onChange={this.onValueMappingsChanged} valueMappings={defaults.mappings} />
|
||||
|
||||
<PanelOptionsGroup title="Data links">
|
||||
<DataLinksEditor
|
||||
|
||||
@@ -6,51 +6,6 @@ Object {
|
||||
"calcs": Array [
|
||||
"last",
|
||||
],
|
||||
"defaults": Object {
|
||||
"color": Object {
|
||||
"mode": "thresholds",
|
||||
},
|
||||
"decimals": 3,
|
||||
"mappings": Array [
|
||||
Object {
|
||||
"from": "50",
|
||||
"id": 1,
|
||||
"operator": "",
|
||||
"text": "BIG",
|
||||
"to": "1000",
|
||||
"type": 2,
|
||||
"value": "",
|
||||
},
|
||||
],
|
||||
"max": "50",
|
||||
"min": "-50",
|
||||
"thresholds": Object {
|
||||
"mode": "absolute",
|
||||
"steps": Array [
|
||||
Object {
|
||||
"color": "green",
|
||||
"index": 0,
|
||||
"value": -Infinity,
|
||||
},
|
||||
Object {
|
||||
"color": "#EAB839",
|
||||
"index": 1,
|
||||
"value": -25,
|
||||
},
|
||||
Object {
|
||||
"color": "#6ED0E0",
|
||||
"index": 2,
|
||||
"value": 0,
|
||||
},
|
||||
Object {
|
||||
"color": "red",
|
||||
"index": 3,
|
||||
"value": 25,
|
||||
},
|
||||
],
|
||||
},
|
||||
"unit": "accMS2",
|
||||
},
|
||||
},
|
||||
"orientation": "auto",
|
||||
"showThresholdLabels": true,
|
||||
|
||||
@@ -2,10 +2,12 @@ import { PanelPlugin } from '@grafana/data';
|
||||
import { GaugePanelEditor } from './GaugePanelEditor';
|
||||
import { GaugePanel } from './GaugePanel';
|
||||
import { GaugeOptions, defaults } from './types';
|
||||
import { standardFieldConfig } from '../stat/types';
|
||||
import { gaugePanelMigrationHandler, gaugePanelChangedHandler } from './GaugeMigrations';
|
||||
|
||||
export const plugin = new PanelPlugin<GaugeOptions>(GaugePanel)
|
||||
.setDefaults(defaults)
|
||||
.setFieldConfigDefaults(standardFieldConfig)
|
||||
.setEditor(GaugePanelEditor)
|
||||
.setPanelChangeHandler(gaugePanelChangedHandler)
|
||||
.setMigrationHandler(gaugePanelMigrationHandler);
|
||||
|
||||
@@ -14,6 +14,7 @@ export const GraphPanel: React.FunctionComponent<GraphPanelProps> = ({
|
||||
width,
|
||||
height,
|
||||
options,
|
||||
fieldConfig,
|
||||
onOptionsChange,
|
||||
onChangeTimeRange,
|
||||
}) => {
|
||||
@@ -43,6 +44,7 @@ export const GraphPanel: React.FunctionComponent<GraphPanelProps> = ({
|
||||
data={data}
|
||||
timeZone={timeZone}
|
||||
options={options}
|
||||
fieldConfig={fieldConfig}
|
||||
onOptionsChange={onOptionsChange}
|
||||
onChangeTimeRange={onChangeTimeRange}
|
||||
>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import React from 'react';
|
||||
import { GraphSeriesToggler } from '@grafana/ui';
|
||||
import { PanelData, GraphSeriesXY, AbsoluteTimeRange, TimeZone } from '@grafana/data';
|
||||
import { PanelData, GraphSeriesXY, AbsoluteTimeRange, TimeZone, FieldConfigSource } from '@grafana/data';
|
||||
|
||||
import { getGraphSeriesModel } from './getGraphSeriesModel';
|
||||
import { Options, SeriesOptions } from './types';
|
||||
@@ -18,6 +18,7 @@ interface GraphPanelControllerAPI {
|
||||
interface GraphPanelControllerProps {
|
||||
children: (api: GraphPanelControllerAPI) => JSX.Element;
|
||||
options: Options;
|
||||
fieldConfig: FieldConfigSource;
|
||||
data: PanelData;
|
||||
timeZone: TimeZone;
|
||||
onOptionsChange: (options: Options) => void;
|
||||
@@ -44,7 +45,7 @@ export class GraphPanelController extends React.Component<GraphPanelControllerPr
|
||||
props.options.series,
|
||||
props.options.graph,
|
||||
props.options.legend,
|
||||
props.options.fieldOptions
|
||||
props.fieldConfig
|
||||
),
|
||||
};
|
||||
}
|
||||
@@ -58,7 +59,7 @@ export class GraphPanelController extends React.Component<GraphPanelControllerPr
|
||||
props.options.series,
|
||||
props.options.graph,
|
||||
props.options.legend,
|
||||
props.options.fieldOptions
|
||||
props.fieldConfig
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -3,15 +3,15 @@ import _ from 'lodash';
|
||||
import React, { PureComponent } from 'react';
|
||||
|
||||
// Types
|
||||
import { PanelEditorProps, FieldConfig } from '@grafana/data';
|
||||
import { FieldConfig, PanelEditorProps } from '@grafana/data';
|
||||
import {
|
||||
Switch,
|
||||
LegendOptions,
|
||||
GraphTooltipOptions,
|
||||
PanelOptionsGrid,
|
||||
PanelOptionsGroup,
|
||||
FieldPropertiesEditor,
|
||||
Select,
|
||||
FieldPropertiesEditor,
|
||||
} from '@grafana/ui';
|
||||
import { Options, GraphOptions } from './types';
|
||||
import { GraphLegendEditor } from './GraphLegendEditor';
|
||||
@@ -47,13 +47,10 @@ export class GraphPanelEditor extends PureComponent<PanelEditorProps<Options>> {
|
||||
this.onGraphOptionsChange({ showPoints: !this.props.options.graph.showPoints });
|
||||
};
|
||||
|
||||
onDefaultsChange = (field: FieldConfig) => {
|
||||
this.props.onOptionsChange({
|
||||
...this.props.options,
|
||||
fieldOptions: {
|
||||
...this.props.options.fieldOptions,
|
||||
defaults: field,
|
||||
},
|
||||
onDefaultsChange = (field: FieldConfig, event?: React.SyntheticEvent<HTMLElement>, callback?: () => void) => {
|
||||
this.props.onFieldConfigChange({
|
||||
...this.props.fieldConfig,
|
||||
defaults: field,
|
||||
});
|
||||
};
|
||||
|
||||
@@ -76,7 +73,7 @@ export class GraphPanelEditor extends PureComponent<PanelEditorProps<Options>> {
|
||||
<FieldPropertiesEditor
|
||||
showMinMax={false}
|
||||
onChange={this.onDefaultsChange}
|
||||
value={this.props.options.fieldOptions.defaults}
|
||||
value={this.props.fieldConfig.defaults}
|
||||
/>
|
||||
</PanelOptionsGroup>
|
||||
<PanelOptionsGroup title="Tooltip">
|
||||
|
||||
@@ -9,7 +9,6 @@ import {
|
||||
GraphSeriesXY,
|
||||
getTimeField,
|
||||
DataFrame,
|
||||
FieldDisplayOptions,
|
||||
getSeriesTimeStep,
|
||||
TimeZone,
|
||||
hasMsResolution,
|
||||
@@ -17,6 +16,7 @@ import {
|
||||
DEFAULT_DATE_TIME_FORMAT,
|
||||
FieldColor,
|
||||
FieldColorMode,
|
||||
FieldConfigSource,
|
||||
} from '@grafana/data';
|
||||
|
||||
import { SeriesOptions, GraphOptions } from './types';
|
||||
@@ -28,7 +28,7 @@ export const getGraphSeriesModel = (
|
||||
seriesOptions: SeriesOptions,
|
||||
graphOptions: GraphOptions,
|
||||
legendOptions: GraphLegendEditorLegendOptions,
|
||||
fieldOptions?: FieldDisplayOptions
|
||||
fieldOptions?: FieldConfigSource
|
||||
) => {
|
||||
const graphs: GraphSeriesXY[] = [];
|
||||
|
||||
|
||||
@@ -16,9 +16,10 @@ interface Props extends PanelProps<PieChartOptions> {}
|
||||
|
||||
export class PieChartPanel extends PureComponent<Props> {
|
||||
render() {
|
||||
const { width, height, options, data, replaceVariables } = this.props;
|
||||
const { width, height, options, data, replaceVariables, fieldConfig } = this.props;
|
||||
|
||||
const values = getFieldDisplayValues({
|
||||
fieldConfig,
|
||||
fieldOptions: options.fieldOptions,
|
||||
data: data.series,
|
||||
theme: config.theme,
|
||||
|
||||
@@ -1,22 +1,25 @@
|
||||
import React, { PureComponent } from 'react';
|
||||
import {
|
||||
PanelOptionsGrid,
|
||||
ValueMappingsEditor,
|
||||
FieldDisplayEditor,
|
||||
FieldPropertiesEditor,
|
||||
PanelOptionsGroup,
|
||||
FieldPropertiesEditor,
|
||||
LegacyValueMappingsEditor,
|
||||
} from '@grafana/ui';
|
||||
import { ValueMapping, FieldConfig, PanelEditorProps, FieldDisplayOptions } from '@grafana/data';
|
||||
import { PanelEditorProps, FieldDisplayOptions, ValueMapping, FieldConfig } from '@grafana/data';
|
||||
|
||||
import { PieChartOptionsBox } from './PieChartOptionsBox';
|
||||
import { PieChartOptions } from './types';
|
||||
|
||||
export class PieChartPanelEditor extends PureComponent<PanelEditorProps<PieChartOptions>> {
|
||||
onValueMappingsChanged = (mappings: ValueMapping[]) => {
|
||||
const current = this.props.options.fieldOptions.defaults;
|
||||
this.onDefaultsChange({
|
||||
const current = this.props.fieldConfig;
|
||||
this.props.onFieldConfigChange({
|
||||
...current,
|
||||
mappings,
|
||||
defaults: {
|
||||
...current.defaults,
|
||||
mappings,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
@@ -27,16 +30,16 @@ export class PieChartPanelEditor extends PureComponent<PanelEditorProps<PieChart
|
||||
});
|
||||
|
||||
onDefaultsChange = (field: FieldConfig) => {
|
||||
this.onDisplayOptionsChanged({
|
||||
...this.props.options.fieldOptions,
|
||||
this.props.onFieldConfigChange({
|
||||
...this.props.fieldConfig,
|
||||
defaults: field,
|
||||
});
|
||||
};
|
||||
|
||||
render() {
|
||||
const { onOptionsChange, options, data } = this.props;
|
||||
const { onOptionsChange, options, data, fieldConfig, onFieldConfigChange } = this.props;
|
||||
const { fieldOptions } = options;
|
||||
const { defaults } = fieldOptions;
|
||||
const { defaults } = fieldConfig;
|
||||
|
||||
return (
|
||||
<>
|
||||
@@ -49,10 +52,15 @@ export class PieChartPanelEditor extends PureComponent<PanelEditorProps<PieChart
|
||||
<FieldPropertiesEditor showMinMax={true} onChange={this.onDefaultsChange} value={defaults} />
|
||||
</PanelOptionsGroup>
|
||||
|
||||
<PieChartOptionsBox data={data} onOptionsChange={onOptionsChange} options={options} />
|
||||
<PieChartOptionsBox
|
||||
data={data}
|
||||
onOptionsChange={onOptionsChange}
|
||||
options={options}
|
||||
fieldConfig={fieldConfig}
|
||||
onFieldConfigChange={onFieldConfigChange}
|
||||
/>
|
||||
</PanelOptionsGrid>
|
||||
|
||||
<ValueMappingsEditor onChange={this.onValueMappingsChanged} valueMappings={defaults.mappings} />
|
||||
<LegacyValueMappingsEditor onChange={this.onValueMappingsChanged} valueMappings={defaults.mappings} />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -5,4 +5,5 @@ import { PieChartOptions, defaults } from './types';
|
||||
|
||||
export const plugin = new PanelPlugin<PieChartOptions>(PieChartPanel)
|
||||
.setDefaults(defaults)
|
||||
.setFieldConfigDefaults({ defaults: { unit: 'short' } })
|
||||
.setEditor(PieChartPanelEditor);
|
||||
|
||||
@@ -14,8 +14,5 @@ export const defaults: PieChartOptions = {
|
||||
fieldOptions: {
|
||||
...standardFieldDisplayOptions,
|
||||
calcs: [ReducerID.last],
|
||||
defaults: {
|
||||
unit: 'short',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
@@ -69,10 +69,11 @@ export class StatPanel extends PureComponent<PanelProps<StatPanelOptions>> {
|
||||
};
|
||||
|
||||
getValues = (): FieldDisplay[] => {
|
||||
const { data, options, replaceVariables } = this.props;
|
||||
const { data, options, replaceVariables, fieldConfig } = this.props;
|
||||
|
||||
return getFieldDisplayValues({
|
||||
...options,
|
||||
fieldConfig,
|
||||
fieldOptions: options.fieldOptions,
|
||||
replaceVariables,
|
||||
theme: config.theme,
|
||||
data: data.series,
|
||||
|
||||
@@ -2,48 +2,53 @@
|
||||
import React, { PureComponent } from 'react';
|
||||
|
||||
import {
|
||||
ThresholdsEditor,
|
||||
PanelOptionsGrid,
|
||||
ValueMappingsEditor,
|
||||
FieldDisplayEditor,
|
||||
FieldPropertiesEditor,
|
||||
PanelOptionsGroup,
|
||||
DataLinksEditor,
|
||||
FormLabel,
|
||||
Select,
|
||||
FieldPropertiesEditor,
|
||||
ThresholdsEditor,
|
||||
LegacyValueMappingsEditor,
|
||||
DataLinksEditor,
|
||||
} from '@grafana/ui';
|
||||
|
||||
import {
|
||||
ThresholdsConfig,
|
||||
ValueMapping,
|
||||
FieldConfig,
|
||||
DataLink,
|
||||
PanelEditorProps,
|
||||
FieldDisplayOptions,
|
||||
FieldConfig,
|
||||
ValueMapping,
|
||||
ThresholdsConfig,
|
||||
DataLink,
|
||||
} from '@grafana/data';
|
||||
|
||||
import { StatPanelOptions, colorModes, graphModes, justifyModes } from './types';
|
||||
import { orientationOptions } from '../gauge/types';
|
||||
|
||||
import {
|
||||
getDataLinksVariableSuggestions,
|
||||
getCalculationValueDataLinksVariableSuggestions,
|
||||
} from 'app/features/panel/panellinks/link_srv';
|
||||
getDataLinksVariableSuggestions,
|
||||
} from '../../../features/panel/panellinks/link_srv';
|
||||
|
||||
export class StatPanelEditor extends PureComponent<PanelEditorProps<StatPanelOptions>> {
|
||||
onThresholdsChanged = (thresholds: ThresholdsConfig) => {
|
||||
const current = this.props.options.fieldOptions.defaults;
|
||||
this.onDefaultsChange({
|
||||
const current = this.props.fieldConfig;
|
||||
this.props.onFieldConfigChange({
|
||||
...current,
|
||||
thresholds,
|
||||
defaults: {
|
||||
...current.defaults,
|
||||
thresholds,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
onValueMappingsChanged = (mappings: ValueMapping[]) => {
|
||||
const current = this.props.options.fieldOptions.defaults;
|
||||
this.onDefaultsChange({
|
||||
const current = this.props.fieldConfig;
|
||||
this.props.onFieldConfigChange({
|
||||
...current,
|
||||
mappings,
|
||||
defaults: {
|
||||
...current.defaults,
|
||||
mappings,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
@@ -59,23 +64,28 @@ export class StatPanelEditor extends PureComponent<PanelEditorProps<StatPanelOpt
|
||||
onOrientationChange = ({ value }: any) => this.props.onOptionsChange({ ...this.props.options, orientation: value });
|
||||
|
||||
onDefaultsChange = (field: FieldConfig) => {
|
||||
this.onDisplayOptionsChanged({
|
||||
...this.props.options.fieldOptions,
|
||||
this.props.onFieldConfigChange({
|
||||
...this.props.fieldConfig,
|
||||
defaults: field,
|
||||
});
|
||||
};
|
||||
|
||||
onDataLinksChanged = (links: DataLink[]) => {
|
||||
this.onDefaultsChange({
|
||||
...this.props.options.fieldOptions.defaults,
|
||||
links,
|
||||
const current = this.props.fieldConfig;
|
||||
this.props.onFieldConfigChange({
|
||||
...current,
|
||||
defaults: {
|
||||
...current.defaults,
|
||||
links,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
render() {
|
||||
const { options } = this.props;
|
||||
const { options, fieldConfig } = this.props;
|
||||
const { fieldOptions } = options;
|
||||
const { defaults } = fieldOptions;
|
||||
const { defaults } = fieldConfig;
|
||||
|
||||
const suggestions = fieldOptions.values
|
||||
? getDataLinksVariableSuggestions(this.props.data.series)
|
||||
: getCalculationValueDataLinksVariableSuggestions(this.props.data.series);
|
||||
@@ -126,7 +136,6 @@ export class StatPanelEditor extends PureComponent<PanelEditorProps<StatPanelOpt
|
||||
/>
|
||||
</div>
|
||||
</PanelOptionsGroup>
|
||||
|
||||
<PanelOptionsGroup title="Field">
|
||||
<FieldPropertiesEditor
|
||||
showMinMax={true}
|
||||
@@ -138,8 +147,7 @@ export class StatPanelEditor extends PureComponent<PanelEditorProps<StatPanelOpt
|
||||
|
||||
<ThresholdsEditor onChange={this.onThresholdsChanged} thresholds={defaults.thresholds} />
|
||||
</PanelOptionsGrid>
|
||||
|
||||
<ValueMappingsEditor onChange={this.onValueMappingsChanged} valueMappings={defaults.mappings} />
|
||||
<LegacyValueMappingsEditor onChange={this.onValueMappingsChanged} valueMappings={defaults.mappings} />
|
||||
|
||||
<PanelOptionsGroup title="Data links">
|
||||
<DataLinksEditor
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
import { sharedSingleStatMigrationHandler, sharedSingleStatPanelChangedHandler } from '@grafana/ui';
|
||||
import { PanelPlugin } from '@grafana/data';
|
||||
import { StatPanelOptions, defaults } from './types';
|
||||
import { StatPanelOptions, defaults, standardFieldConfig } from './types';
|
||||
import { StatPanel } from './StatPanel';
|
||||
import { StatPanelEditor } from './StatPanelEditor';
|
||||
|
||||
export const plugin = new PanelPlugin<StatPanelOptions>(StatPanel)
|
||||
.setDefaults(defaults)
|
||||
.setFieldConfigDefaults(standardFieldConfig)
|
||||
.setEditor(StatPanelEditor)
|
||||
.setNoPadding()
|
||||
.setPanelChangeHandler(sharedSingleStatPanelChangedHandler)
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
import { SingleStatBaseOptions, BigValueColorMode, BigValueGraphMode, BigValueJustifyMode } from '@grafana/ui';
|
||||
import { VizOrientation, ReducerID, FieldDisplayOptions, SelectableValue, ThresholdsMode } from '@grafana/data';
|
||||
import {
|
||||
VizOrientation,
|
||||
ReducerID,
|
||||
FieldDisplayOptions,
|
||||
SelectableValue,
|
||||
FieldConfigSource,
|
||||
ThresholdsMode,
|
||||
} from '@grafana/data';
|
||||
|
||||
// Structure copied from angular
|
||||
export interface StatPanelOptions extends SingleStatBaseOptions {
|
||||
@@ -26,6 +33,9 @@ export const justifyModes: Array<SelectableValue<BigValueJustifyMode>> = [
|
||||
export const standardFieldDisplayOptions: FieldDisplayOptions = {
|
||||
values: false,
|
||||
calcs: [ReducerID.mean],
|
||||
};
|
||||
|
||||
export const standardFieldConfig: FieldConfigSource = {
|
||||
defaults: {
|
||||
thresholds: {
|
||||
mode: ThresholdsMode.Absolute,
|
||||
|
||||
@@ -1,14 +1,7 @@
|
||||
import { FieldConfigSource } from '@grafana/data';
|
||||
|
||||
export interface Options {
|
||||
fieldOptions: FieldConfigSource;
|
||||
showHeader: boolean;
|
||||
}
|
||||
|
||||
export const defaults: Options = {
|
||||
fieldOptions: {
|
||||
defaults: {},
|
||||
overrides: [],
|
||||
},
|
||||
showHeader: true,
|
||||
};
|
||||
|
||||
BIN
public/e2e-tests/videos/queryVariableCrud.spec.ts.mp4
Normal file
BIN
public/e2e-tests/videos/queryVariableCrud.spec.ts.mp4
Normal file
Binary file not shown.
BIN
public/e2e-tests/videos/smoketests.spec.ts.mp4
Normal file
BIN
public/e2e-tests/videos/smoketests.spec.ts.mp4
Normal file
Binary file not shown.
Reference in New Issue
Block a user