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:
@@ -28,7 +28,9 @@ describe('FieldDisplay', () => {
|
|||||||
const options = createDisplayOptions({
|
const options = createDisplayOptions({
|
||||||
fieldOptions: {
|
fieldOptions: {
|
||||||
calcs: [ReducerID.first],
|
calcs: [ReducerID.first],
|
||||||
override: {},
|
},
|
||||||
|
fieldConfig: {
|
||||||
|
overrides: [],
|
||||||
defaults: {
|
defaults: {
|
||||||
title: '$__cell_0 * $__field_name * $__series_name',
|
title: '$__cell_0 * $__field_name * $__series_name',
|
||||||
},
|
},
|
||||||
@@ -42,8 +44,6 @@ describe('FieldDisplay', () => {
|
|||||||
const options = createDisplayOptions({
|
const options = createDisplayOptions({
|
||||||
fieldOptions: {
|
fieldOptions: {
|
||||||
calcs: [ReducerID.last],
|
calcs: [ReducerID.last],
|
||||||
override: {},
|
|
||||||
defaults: {},
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
const display = getFieldDisplayValues(options);
|
const display = getFieldDisplayValues(options);
|
||||||
@@ -56,8 +56,6 @@ describe('FieldDisplay', () => {
|
|||||||
values: true, //
|
values: true, //
|
||||||
limit: 1000,
|
limit: 1000,
|
||||||
calcs: [],
|
calcs: [],
|
||||||
override: {},
|
|
||||||
defaults: {},
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
const display = getFieldDisplayValues(options);
|
const display = getFieldDisplayValues(options);
|
||||||
@@ -70,8 +68,6 @@ describe('FieldDisplay', () => {
|
|||||||
values: true, //
|
values: true, //
|
||||||
limit: 2,
|
limit: 2,
|
||||||
calcs: [],
|
calcs: [],
|
||||||
override: {},
|
|
||||||
defaults: {},
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
const display = getFieldDisplayValues(options);
|
const display = getFieldDisplayValues(options);
|
||||||
@@ -101,7 +97,7 @@ describe('FieldDisplay', () => {
|
|||||||
|
|
||||||
it('Should return field thresholds when there is no data', () => {
|
it('Should return field thresholds when there is no data', () => {
|
||||||
const options = createEmptyDisplayOptions({
|
const options = createEmptyDisplayOptions({
|
||||||
fieldOptions: {
|
fieldConfig: {
|
||||||
defaults: {
|
defaults: {
|
||||||
thresholds: { steps: [{ color: '#F2495C', value: 50 }] },
|
thresholds: { steps: [{ color: '#F2495C', value: 50 }] },
|
||||||
},
|
},
|
||||||
@@ -123,7 +119,7 @@ describe('FieldDisplay', () => {
|
|||||||
it('Should return field mapped value when there is no data', () => {
|
it('Should return field mapped value when there is no data', () => {
|
||||||
const mapEmptyToText = '0';
|
const mapEmptyToText = '0';
|
||||||
const options = createEmptyDisplayOptions({
|
const options = createEmptyDisplayOptions({
|
||||||
fieldOptions: {
|
fieldConfig: {
|
||||||
defaults: {
|
defaults: {
|
||||||
mappings: [
|
mappings: [
|
||||||
{
|
{
|
||||||
@@ -146,8 +142,8 @@ describe('FieldDisplay', () => {
|
|||||||
it('Should always return display numeric 0 when there is no data', () => {
|
it('Should always return display numeric 0 when there is no data', () => {
|
||||||
const mapEmptyToText = '0';
|
const mapEmptyToText = '0';
|
||||||
const options = createEmptyDisplayOptions({
|
const options = createEmptyDisplayOptions({
|
||||||
fieldOptions: {
|
fieldConfig: {
|
||||||
override: {
|
overrides: {
|
||||||
mappings: [
|
mappings: [
|
||||||
{
|
{
|
||||||
id: 1,
|
id: 1,
|
||||||
@@ -241,7 +237,7 @@ function createEmptyDisplayOptions(extend = {}): GetFieldDisplayValuesOptions {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function createDisplayOptions(extend = {}): GetFieldDisplayValuesOptions {
|
function createDisplayOptions(extend: Partial<GetFieldDisplayValuesOptions> = {}): GetFieldDisplayValuesOptions {
|
||||||
const options: GetFieldDisplayValuesOptions = {
|
const options: GetFieldDisplayValuesOptions = {
|
||||||
data: [
|
data: [
|
||||||
toDataFrame({
|
toDataFrame({
|
||||||
@@ -258,8 +254,10 @@ function createDisplayOptions(extend = {}): GetFieldDisplayValuesOptions {
|
|||||||
},
|
},
|
||||||
fieldOptions: {
|
fieldOptions: {
|
||||||
calcs: [],
|
calcs: [],
|
||||||
defaults: {},
|
},
|
||||||
|
fieldConfig: {
|
||||||
overrides: [],
|
overrides: [],
|
||||||
|
defaults: {},
|
||||||
},
|
},
|
||||||
theme: {} as GrafanaTheme,
|
theme: {} as GrafanaTheme,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -19,7 +19,8 @@ import { ReducerID, reduceField } from '../transformations/fieldReducer';
|
|||||||
import { ScopedVars } from '../types/ScopedVars';
|
import { ScopedVars } from '../types/ScopedVars';
|
||||||
import { getTimeField } from '../dataframe/processDataFrame';
|
import { getTimeField } from '../dataframe/processDataFrame';
|
||||||
|
|
||||||
export interface FieldDisplayOptions extends FieldConfigSource {
|
// export interface FieldDisplayOptions extends FieldConfigSource {
|
||||||
|
export interface FieldDisplayOptions {
|
||||||
values?: boolean; // If true show each row value
|
values?: boolean; // If true show each row value
|
||||||
limit?: number; // if showing all values limit
|
limit?: number; // if showing all values limit
|
||||||
calcs: string[]; // when !values, pick one value for the whole field
|
calcs: string[]; // when !values, pick one value for the whole field
|
||||||
@@ -57,6 +58,7 @@ function getTitleTemplate(title: string | undefined, stats: string[], data?: Dat
|
|||||||
if (fieldCount > 1 || !parts.length) {
|
if (fieldCount > 1 || !parts.length) {
|
||||||
parts.push('${' + VAR_FIELD_NAME + '}');
|
parts.push('${' + VAR_FIELD_NAME + '}');
|
||||||
}
|
}
|
||||||
|
|
||||||
return parts.join(' ');
|
return parts.join(' ');
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -75,6 +77,7 @@ export interface FieldDisplay {
|
|||||||
export interface GetFieldDisplayValuesOptions {
|
export interface GetFieldDisplayValuesOptions {
|
||||||
data?: DataFrame[];
|
data?: DataFrame[];
|
||||||
fieldOptions: FieldDisplayOptions;
|
fieldOptions: FieldDisplayOptions;
|
||||||
|
fieldConfig: FieldConfigSource;
|
||||||
replaceVariables: InterpolateFunction;
|
replaceVariables: InterpolateFunction;
|
||||||
sparkline?: boolean; // Calculate the sparkline
|
sparkline?: boolean; // Calculate the sparkline
|
||||||
theme: GrafanaTheme;
|
theme: GrafanaTheme;
|
||||||
@@ -84,7 +87,7 @@ export interface GetFieldDisplayValuesOptions {
|
|||||||
export const DEFAULT_FIELD_DISPLAY_VALUES_LIMIT = 25;
|
export const DEFAULT_FIELD_DISPLAY_VALUES_LIMIT = 25;
|
||||||
|
|
||||||
export const getFieldDisplayValues = (options: GetFieldDisplayValuesOptions): FieldDisplay[] => {
|
export const getFieldDisplayValues = (options: GetFieldDisplayValuesOptions): FieldDisplay[] => {
|
||||||
const { replaceVariables, fieldOptions } = options;
|
const { replaceVariables, fieldOptions, fieldConfig } = options;
|
||||||
const calcs = fieldOptions.calcs.length ? fieldOptions.calcs : [ReducerID.last];
|
const calcs = fieldOptions.calcs.length ? fieldOptions.calcs : [ReducerID.last];
|
||||||
|
|
||||||
const values: FieldDisplay[] = [];
|
const values: FieldDisplay[] = [];
|
||||||
@@ -94,7 +97,7 @@ export const getFieldDisplayValues = (options: GetFieldDisplayValuesOptions): Fi
|
|||||||
const data = options.data;
|
const data = options.data;
|
||||||
let hitLimit = false;
|
let hitLimit = false;
|
||||||
const limit = fieldOptions.limit ? fieldOptions.limit : DEFAULT_FIELD_DISPLAY_VALUES_LIMIT;
|
const limit = fieldOptions.limit ? fieldOptions.limit : DEFAULT_FIELD_DISPLAY_VALUES_LIMIT;
|
||||||
const defaultTitle = getTitleTemplate(fieldOptions.defaults.title, calcs, data);
|
const defaultTitle = getTitleTemplate(fieldConfig.defaults.title, calcs, data);
|
||||||
const scopedVars: ScopedVars = {};
|
const scopedVars: ScopedVars = {};
|
||||||
|
|
||||||
for (let s = 0; s < data.length && !hitLimit; s++) {
|
for (let s = 0; s < data.length && !hitLimit; s++) {
|
||||||
@@ -194,7 +197,7 @@ export const getFieldDisplayValues = (options: GetFieldDisplayValuesOptions): Fi
|
|||||||
|
|
||||||
if (values.length === 0) {
|
if (values.length === 0) {
|
||||||
values.push(createNoValuesFieldDisplay(options));
|
values.push(createNoValuesFieldDisplay(options));
|
||||||
} else if (values.length === 1 && !fieldOptions.defaults.title) {
|
} else if (values.length === 1 && !fieldConfig.defaults.title) {
|
||||||
// Don't show title for single item
|
// Don't show title for single item
|
||||||
values[0].display.title = undefined;
|
values[0].display.title = undefined;
|
||||||
}
|
}
|
||||||
@@ -237,8 +240,8 @@ export function getDisplayValueAlignmentFactors(values: FieldDisplay[]): Display
|
|||||||
|
|
||||||
function createNoValuesFieldDisplay(options: GetFieldDisplayValuesOptions): FieldDisplay {
|
function createNoValuesFieldDisplay(options: GetFieldDisplayValuesOptions): FieldDisplay {
|
||||||
const displayName = 'No data';
|
const displayName = 'No data';
|
||||||
const { fieldOptions } = options;
|
const { fieldConfig } = options;
|
||||||
const { defaults } = fieldOptions;
|
const { defaults } = fieldConfig;
|
||||||
|
|
||||||
const displayProcessor = getDisplayProcessor({
|
const displayProcessor = getDisplayProcessor({
|
||||||
field: {
|
field: {
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import { ScopedVars } from './ScopedVars';
|
|||||||
import { LoadingState } from './data';
|
import { LoadingState } from './data';
|
||||||
import { DataFrame } from './dataFrame';
|
import { DataFrame } from './dataFrame';
|
||||||
import { AbsoluteTimeRange, TimeRange, TimeZone } from './time';
|
import { AbsoluteTimeRange, TimeRange, TimeZone } from './time';
|
||||||
import { FieldConfigEditorRegistry } from './fieldOverrides';
|
import { FieldConfigEditorRegistry, FieldConfigSource } from './fieldOverrides';
|
||||||
|
|
||||||
export type InterpolateFunction = (value: string, scopedVars?: ScopedVars, format?: string | Function) => string;
|
export type InterpolateFunction = (value: string, scopedVars?: ScopedVars, format?: string | Function) => string;
|
||||||
|
|
||||||
@@ -54,6 +54,10 @@ export interface PanelProps<T = any> {
|
|||||||
timeZone: TimeZone;
|
timeZone: TimeZone;
|
||||||
options: T;
|
options: T;
|
||||||
onOptionsChange: (options: T) => void;
|
onOptionsChange: (options: T) => void;
|
||||||
|
/** Panel fields configuration */
|
||||||
|
fieldConfig: FieldConfigSource;
|
||||||
|
/** Enables panel field config manipulation */
|
||||||
|
onFieldConfigChange: (config: FieldConfigSource) => void;
|
||||||
renderCounter: number;
|
renderCounter: number;
|
||||||
transparent: boolean;
|
transparent: boolean;
|
||||||
width: number;
|
width: number;
|
||||||
@@ -70,11 +74,23 @@ export interface PanelEditorProps<T = any> {
|
|||||||
callback?: () => void
|
callback?: () => void
|
||||||
) => void;
|
) => void;
|
||||||
data: PanelData;
|
data: PanelData;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Panel fields configuration - temporart solution
|
||||||
|
* TODO[FieldConfig]: Remove when we switch old editor to new
|
||||||
|
*/
|
||||||
|
fieldConfig: FieldConfigSource;
|
||||||
|
/**
|
||||||
|
* Enables panel field config manipulation
|
||||||
|
* TODO[FieldConfig]: Remove when we switch old editor to new
|
||||||
|
*/
|
||||||
|
onFieldConfigChange: (config: FieldConfigSource) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface PanelModel<TOptions = any> {
|
export interface PanelModel<TOptions = any> {
|
||||||
id: number;
|
id: number;
|
||||||
options: TOptions;
|
options: TOptions;
|
||||||
|
fieldConfig: FieldConfigSource;
|
||||||
pluginVersion?: string;
|
pluginVersion?: string;
|
||||||
scopedVars?: ScopedVars;
|
scopedVars?: ScopedVars;
|
||||||
}
|
}
|
||||||
@@ -98,6 +114,10 @@ export class PanelPlugin<TOptions = any> extends GrafanaPlugin<PanelPluginMeta>
|
|||||||
editor?: ComponentClass<PanelEditorProps<TOptions>>;
|
editor?: ComponentClass<PanelEditorProps<TOptions>>;
|
||||||
customFieldConfigs?: FieldConfigEditorRegistry;
|
customFieldConfigs?: FieldConfigEditorRegistry;
|
||||||
defaults?: TOptions;
|
defaults?: TOptions;
|
||||||
|
fieldConfigDefaults?: FieldConfigSource = {
|
||||||
|
defaults: {},
|
||||||
|
overrides: [],
|
||||||
|
};
|
||||||
onPanelMigration?: PanelMigrationHandler<TOptions>;
|
onPanelMigration?: PanelMigrationHandler<TOptions>;
|
||||||
onPanelTypeChanged?: PanelTypeChangedHandler<TOptions>;
|
onPanelTypeChanged?: PanelTypeChangedHandler<TOptions>;
|
||||||
noPadding?: boolean;
|
noPadding?: boolean;
|
||||||
@@ -155,6 +175,19 @@ export class PanelPlugin<TOptions = any> extends GrafanaPlugin<PanelPluginMeta>
|
|||||||
this.customFieldConfigs = registry;
|
this.customFieldConfigs = registry;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enables configuration of panel's default field config
|
||||||
|
*/
|
||||||
|
setFieldConfigDefaults(defaultConfig: Partial<FieldConfigSource>) {
|
||||||
|
this.fieldConfigDefaults = {
|
||||||
|
defaults: {},
|
||||||
|
overrides: [],
|
||||||
|
...defaultConfig,
|
||||||
|
};
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface PanelMenuItem {
|
export interface PanelMenuItem {
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ import {
|
|||||||
InterpolateFunction,
|
InterpolateFunction,
|
||||||
GrafanaTheme,
|
GrafanaTheme,
|
||||||
FieldMatcherID,
|
FieldMatcherID,
|
||||||
FieldDisplayOptions,
|
|
||||||
MutableDataFrame,
|
MutableDataFrame,
|
||||||
DataFrame,
|
DataFrame,
|
||||||
toDataFrame,
|
toDataFrame,
|
||||||
@@ -82,7 +81,7 @@ describe('FieldOverrides', () => {
|
|||||||
it('will apply field overrides', () => {
|
it('will apply field overrides', () => {
|
||||||
const data = applyFieldOverrides({
|
const data = applyFieldOverrides({
|
||||||
data: [f0], // the frame
|
data: [f0], // the frame
|
||||||
fieldOptions: src as FieldDisplayOptions, // defaults + overrides
|
fieldOptions: src as FieldConfigSource, // defaults + overrides
|
||||||
replaceVariables: (undefined as any) as InterpolateFunction,
|
replaceVariables: (undefined as any) as InterpolateFunction,
|
||||||
theme: (undefined as any) as GrafanaTheme,
|
theme: (undefined as any) as GrafanaTheme,
|
||||||
})[0];
|
})[0];
|
||||||
@@ -108,7 +107,7 @@ describe('FieldOverrides', () => {
|
|||||||
it('will apply set min/max when asked', () => {
|
it('will apply set min/max when asked', () => {
|
||||||
const data = applyFieldOverrides({
|
const data = applyFieldOverrides({
|
||||||
data: [f0], // the frame
|
data: [f0], // the frame
|
||||||
fieldOptions: src as FieldDisplayOptions, // defaults + overrides
|
fieldOptions: src as FieldConfigSource, // defaults + overrides
|
||||||
replaceVariables: (undefined as any) as InterpolateFunction,
|
replaceVariables: (undefined as any) as InterpolateFunction,
|
||||||
theme: (undefined as any) as GrafanaTheme,
|
theme: (undefined as any) as GrafanaTheme,
|
||||||
autoMinMax: true,
|
autoMinMax: true,
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
import { FieldOverrideContext, FieldOverrideEditorProps, FieldConfigEditorProps, ValueMapping } from '@grafana/data';
|
import { FieldOverrideContext, FieldOverrideEditorProps, FieldConfigEditorProps, ValueMapping } from '@grafana/data';
|
||||||
import { ValueMappingsEditor } from '..';
|
import { LegacyValueMappingsEditor } from '..';
|
||||||
|
|
||||||
export interface ValueMappingFieldConfigSettings {}
|
export interface ValueMappingFieldConfigSettings {}
|
||||||
|
|
||||||
@@ -27,7 +27,7 @@ export class ValueMappingsValueEditor extends React.PureComponent<
|
|||||||
value = [];
|
value = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
return <ValueMappingsEditor valueMappings={value} onChange={onChange} />;
|
return <LegacyValueMappingsEditor valueMappings={value} onChange={onChange} />;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -15,7 +15,6 @@ import {
|
|||||||
toNumberString,
|
toNumberString,
|
||||||
toIntegerOrUndefined,
|
toIntegerOrUndefined,
|
||||||
SelectableValue,
|
SelectableValue,
|
||||||
FieldConfig,
|
|
||||||
} from '@grafana/data';
|
} from '@grafana/data';
|
||||||
|
|
||||||
const showOptions: Array<SelectableValue<boolean>> = [
|
const showOptions: Array<SelectableValue<boolean>> = [
|
||||||
@@ -47,10 +46,6 @@ export class FieldDisplayEditor extends PureComponent<Props> {
|
|||||||
this.props.onChange({ ...this.props.value, calcs });
|
this.props.onChange({ ...this.props.value, calcs });
|
||||||
};
|
};
|
||||||
|
|
||||||
onDefaultsChange = (value: FieldConfig) => {
|
|
||||||
this.props.onChange({ ...this.props.value, defaults: value });
|
|
||||||
};
|
|
||||||
|
|
||||||
onLimitChange = (event: ChangeEvent<HTMLInputElement>) => {
|
onLimitChange = (event: ChangeEvent<HTMLInputElement>) => {
|
||||||
this.props.onChange({
|
this.props.onChange({
|
||||||
...this.props.value,
|
...this.props.value,
|
||||||
|
|||||||
@@ -34,7 +34,48 @@ describe('sharedSingleStatMigrationHandler', () => {
|
|||||||
type: 'bargauge',
|
type: 'bargauge',
|
||||||
};
|
};
|
||||||
|
|
||||||
expect(sharedSingleStatMigrationHandler(panel as any)).toMatchSnapshot();
|
sharedSingleStatMigrationHandler(panel as any);
|
||||||
|
expect((panel as any).fieldConfig).toMatchInlineSnapshot(`
|
||||||
|
Object {
|
||||||
|
"defaults": Object {
|
||||||
|
"color": Object {
|
||||||
|
"mode": "thresholds",
|
||||||
|
},
|
||||||
|
"decimals": 5,
|
||||||
|
"mappings": Array [
|
||||||
|
Object {
|
||||||
|
"text": "OK",
|
||||||
|
"type": 1,
|
||||||
|
"value": "1",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"max": 100,
|
||||||
|
"min": 10,
|
||||||
|
"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 [],
|
||||||
|
}
|
||||||
|
`);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('move thresholds to scale', () => {
|
it('move thresholds to scale', () => {
|
||||||
@@ -64,7 +105,17 @@ describe('sharedSingleStatMigrationHandler', () => {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
expect(sharedSingleStatMigrationHandler(panel as any)).toMatchSnapshot();
|
sharedSingleStatMigrationHandler(panel as any);
|
||||||
|
|
||||||
|
expect((panel as any).fieldConfig).toMatchInlineSnapshot(`
|
||||||
|
Object {
|
||||||
|
"defaults": Object {
|
||||||
|
"mappings": undefined,
|
||||||
|
"thresholds": undefined,
|
||||||
|
},
|
||||||
|
"overrides": Array [],
|
||||||
|
}
|
||||||
|
`);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Remove unused `overrides` option', () => {
|
it('Remove unused `overrides` option', () => {
|
||||||
@@ -90,6 +141,17 @@ describe('sharedSingleStatMigrationHandler', () => {
|
|||||||
type: 'bargauge',
|
type: 'bargauge',
|
||||||
};
|
};
|
||||||
|
|
||||||
expect(sharedSingleStatMigrationHandler(panel as any)).toMatchSnapshot();
|
sharedSingleStatMigrationHandler(panel as any);
|
||||||
|
expect((panel as any).fieldConfig).toMatchInlineSnapshot(`
|
||||||
|
Object {
|
||||||
|
"defaults": Object {
|
||||||
|
"mappings": undefined,
|
||||||
|
"max": 100,
|
||||||
|
"min": 0,
|
||||||
|
"thresholds": undefined,
|
||||||
|
},
|
||||||
|
"overrides": Array [],
|
||||||
|
}
|
||||||
|
`);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -12,7 +12,6 @@ import {
|
|||||||
VizOrientation,
|
VizOrientation,
|
||||||
PanelModel,
|
PanelModel,
|
||||||
FieldDisplayOptions,
|
FieldDisplayOptions,
|
||||||
ConfigOverrideRule,
|
|
||||||
ThresholdsMode,
|
ThresholdsMode,
|
||||||
ThresholdsConfig,
|
ThresholdsConfig,
|
||||||
validateFieldConfig,
|
validateFieldConfig,
|
||||||
@@ -32,66 +31,15 @@ export function sharedSingleStatPanelChangedHandler(
|
|||||||
prevOptions: any
|
prevOptions: any
|
||||||
) {
|
) {
|
||||||
let options = panel.options;
|
let options = panel.options;
|
||||||
|
|
||||||
|
panel.fieldConfig = panel.fieldConfig || {
|
||||||
|
defaults: {},
|
||||||
|
overrides: [],
|
||||||
|
};
|
||||||
|
|
||||||
// Migrating from angular singlestat
|
// Migrating from angular singlestat
|
||||||
if (prevPluginId === 'singlestat' && prevOptions.angular) {
|
if (prevPluginId === 'singlestat' && prevOptions.angular) {
|
||||||
const prevPanel = prevOptions.angular;
|
return migrateFromAngularSinglestat(panel, prevOptions);
|
||||||
const reducer = fieldReducers.getIfExists(prevPanel.valueName);
|
|
||||||
options = {
|
|
||||||
fieldOptions: {
|
|
||||||
defaults: {} as FieldConfig,
|
|
||||||
overrides: [] as ConfigOverrideRule[],
|
|
||||||
calcs: [reducer ? reducer.id : ReducerID.mean],
|
|
||||||
},
|
|
||||||
orientation: VizOrientation.Horizontal,
|
|
||||||
};
|
|
||||||
|
|
||||||
const defaults = options.fieldOptions.defaults;
|
|
||||||
if (prevPanel.format) {
|
|
||||||
defaults.unit = prevPanel.format;
|
|
||||||
}
|
|
||||||
if (prevPanel.nullPointMode) {
|
|
||||||
defaults.nullValueMode = prevPanel.nullPointMode;
|
|
||||||
}
|
|
||||||
if (prevPanel.nullText) {
|
|
||||||
defaults.noValue = prevPanel.nullText;
|
|
||||||
}
|
|
||||||
if (prevPanel.decimals || prevPanel.decimals === 0) {
|
|
||||||
defaults.decimals = prevPanel.decimals;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Convert thresholds and color values
|
|
||||||
if (prevPanel.thresholds && prevPanel.colors) {
|
|
||||||
const levels = prevPanel.thresholds.split(',').map((strVale: string) => {
|
|
||||||
return Number(strVale.trim());
|
|
||||||
});
|
|
||||||
|
|
||||||
// One more color than threshold
|
|
||||||
const thresholds: Threshold[] = [];
|
|
||||||
for (const color of prevPanel.colors) {
|
|
||||||
const idx = thresholds.length - 1;
|
|
||||||
if (idx >= 0) {
|
|
||||||
thresholds.push({ value: levels[idx], color });
|
|
||||||
} else {
|
|
||||||
thresholds.push({ value: -Infinity, color });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
defaults.thresholds = {
|
|
||||||
mode: ThresholdsMode.Absolute,
|
|
||||||
steps: thresholds,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// Convert value mappings
|
|
||||||
const mappings = convertOldAngularValueMapping(prevPanel);
|
|
||||||
if (mappings && mappings.length) {
|
|
||||||
defaults.mappings = mappings;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (prevPanel.gauge && prevPanel.gauge.show) {
|
|
||||||
defaults.min = prevPanel.gauge.minValue;
|
|
||||||
defaults.max = prevPanel.gauge.maxValue;
|
|
||||||
}
|
|
||||||
return options;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const k of optionsToKeep) {
|
for (const k of optionsToKeep) {
|
||||||
@@ -99,6 +47,70 @@ export function sharedSingleStatPanelChangedHandler(
|
|||||||
options[k] = cloneDeep(prevOptions[k]);
|
options[k] = cloneDeep(prevOptions[k]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return options;
|
||||||
|
}
|
||||||
|
|
||||||
|
function migrateFromAngularSinglestat(panel: PanelModel<Partial<SingleStatBaseOptions>> | any, prevOptions: any) {
|
||||||
|
const prevPanel = prevOptions.angular;
|
||||||
|
const reducer = fieldReducers.getIfExists(prevPanel.valueName);
|
||||||
|
const options = {
|
||||||
|
fieldOptions: {
|
||||||
|
calcs: [reducer ? reducer.id : ReducerID.mean],
|
||||||
|
},
|
||||||
|
orientation: VizOrientation.Horizontal,
|
||||||
|
};
|
||||||
|
|
||||||
|
const defaults: FieldConfig = {};
|
||||||
|
|
||||||
|
if (prevPanel.format) {
|
||||||
|
defaults.unit = prevPanel.format;
|
||||||
|
}
|
||||||
|
if (prevPanel.nullPointMode) {
|
||||||
|
defaults.nullValueMode = prevPanel.nullPointMode;
|
||||||
|
}
|
||||||
|
if (prevPanel.nullText) {
|
||||||
|
defaults.noValue = prevPanel.nullText;
|
||||||
|
}
|
||||||
|
if (prevPanel.decimals || prevPanel.decimals === 0) {
|
||||||
|
defaults.decimals = prevPanel.decimals;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert thresholds and color values
|
||||||
|
if (prevPanel.thresholds && prevPanel.colors) {
|
||||||
|
const levels = prevPanel.thresholds.split(',').map((strVale: string) => {
|
||||||
|
return Number(strVale.trim());
|
||||||
|
});
|
||||||
|
|
||||||
|
// One more color than threshold
|
||||||
|
const thresholds: Threshold[] = [];
|
||||||
|
for (const color of prevPanel.colors) {
|
||||||
|
const idx = thresholds.length - 1;
|
||||||
|
if (idx >= 0) {
|
||||||
|
thresholds.push({ value: levels[idx], color });
|
||||||
|
} else {
|
||||||
|
thresholds.push({ value: -Infinity, color });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
defaults.thresholds = {
|
||||||
|
mode: ThresholdsMode.Absolute,
|
||||||
|
steps: thresholds,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert value mappings
|
||||||
|
const mappings = convertOldAngularValueMapping(prevPanel);
|
||||||
|
if (mappings && mappings.length) {
|
||||||
|
defaults.mappings = mappings;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (prevPanel.gauge && prevPanel.gauge.show) {
|
||||||
|
defaults.min = prevPanel.gauge.minValue;
|
||||||
|
defaults.max = prevPanel.gauge.maxValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
panel.fieldConfig.defaults = defaults;
|
||||||
|
|
||||||
return options;
|
return options;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -162,6 +174,22 @@ export function sharedSingleStatMigrationHandler(panel: PanelModel<SingleStatBas
|
|||||||
validateFieldConfig(defaults);
|
validateFieldConfig(defaults);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (previousVersion < 7.0) {
|
||||||
|
panel.fieldConfig = panel.fieldConfig || { defaults: {}, overrides: [] };
|
||||||
|
panel.fieldConfig = {
|
||||||
|
defaults:
|
||||||
|
options.fieldOptions && options.fieldOptions.defaults
|
||||||
|
? { ...panel.fieldConfig.defaults, ...options.fieldOptions.defaults }
|
||||||
|
: panel.fieldConfig.defaults,
|
||||||
|
overrides:
|
||||||
|
options.fieldOptions && options.fieldOptions.overrides
|
||||||
|
? [...panel.fieldConfig.overrides, ...options.fieldOptions.overrides]
|
||||||
|
: panel.fieldConfig.overrides,
|
||||||
|
};
|
||||||
|
delete options.fieldOptions.defaults;
|
||||||
|
delete options.fieldOptions.overrides;
|
||||||
|
}
|
||||||
|
|
||||||
return options as SingleStatBaseOptions;
|
return options as SingleStatBaseOptions;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,75 +0,0 @@
|
|||||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
|
||||||
|
|
||||||
exports[`sharedSingleStatMigrationHandler Remove unused \`overrides\` option 1`] = `
|
|
||||||
Object {
|
|
||||||
"fieldOptions": Object {
|
|
||||||
"decimals": 5,
|
|
||||||
"defaults": Object {
|
|
||||||
"mappings": undefined,
|
|
||||||
"max": 100,
|
|
||||||
"min": 0,
|
|
||||||
"thresholds": undefined,
|
|
||||||
},
|
|
||||||
"overrides": Array [],
|
|
||||||
"stat": "last",
|
|
||||||
"unit": "watt",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`sharedSingleStatMigrationHandler from old valueOptions model without pluginVersion 1`] = `
|
|
||||||
Object {
|
|
||||||
"fieldOptions": Object {
|
|
||||||
"calcs": Array [
|
|
||||||
"last",
|
|
||||||
],
|
|
||||||
"defaults": Object {
|
|
||||||
"color": Object {
|
|
||||||
"mode": "thresholds",
|
|
||||||
},
|
|
||||||
"decimals": 5,
|
|
||||||
"mappings": Array [
|
|
||||||
Object {
|
|
||||||
"text": "OK",
|
|
||||||
"type": 1,
|
|
||||||
"value": "1",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
"max": 100,
|
|
||||||
"min": 10,
|
|
||||||
"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",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`sharedSingleStatMigrationHandler move thresholds to scale 1`] = `
|
|
||||||
Object {
|
|
||||||
"fieldOptions": Object {
|
|
||||||
"defaults": Object {
|
|
||||||
"mappings": undefined,
|
|
||||||
"thresholds": undefined,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
@@ -58,7 +58,7 @@ export class StatsPicker extends PureComponent<Props> {
|
|||||||
if (isArray(item)) {
|
if (isArray(item)) {
|
||||||
onChange(item.map(v => v.value));
|
onChange(item.map(v => v.value));
|
||||||
} else {
|
} else {
|
||||||
onChange(item.value ? [item.value] : []);
|
onChange(item && item.value ? [item.value] : []);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ const mappingOptions = [
|
|||||||
{ value: MappingType.RangeToText, label: 'Range' },
|
{ value: MappingType.RangeToText, label: 'Range' },
|
||||||
];
|
];
|
||||||
|
|
||||||
export default class MappingRow extends PureComponent<Props, State> {
|
export default class LegacyMappingRow extends PureComponent<Props, State> {
|
||||||
constructor(props: Props) {
|
constructor(props: Props) {
|
||||||
super(props);
|
super(props);
|
||||||
|
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { storiesOf } from '@storybook/react';
|
||||||
|
import { action } from '@storybook/addon-actions';
|
||||||
|
import { LegacyValueMappingsEditor } from './LegacyValueMappingsEditor';
|
||||||
|
|
||||||
|
const ValueMappingsEditorStories = storiesOf('Panel/LegacyValueMappingsEditor', module);
|
||||||
|
|
||||||
|
ValueMappingsEditorStories.add('default', () => {
|
||||||
|
return <LegacyValueMappingsEditor valueMappings={[]} onChange={action('Mapping changed')} />;
|
||||||
|
});
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { shallow } from 'enzyme';
|
import { shallow } from 'enzyme';
|
||||||
|
|
||||||
import { ValueMappingsEditor, Props } from './ValueMappingsEditor';
|
import { LegacyValueMappingsEditor, Props } from './LegacyValueMappingsEditor';
|
||||||
import { MappingType } from '@grafana/data';
|
import { MappingType } from '@grafana/data';
|
||||||
|
|
||||||
const setup = (propOverrides?: object) => {
|
const setup = (propOverrides?: object) => {
|
||||||
@@ -15,9 +15,9 @@ const setup = (propOverrides?: object) => {
|
|||||||
|
|
||||||
Object.assign(props, propOverrides);
|
Object.assign(props, propOverrides);
|
||||||
|
|
||||||
const wrapper = shallow(<ValueMappingsEditor {...props} />);
|
const wrapper = shallow(<LegacyValueMappingsEditor {...props} />);
|
||||||
|
|
||||||
const instance = wrapper.instance() as ValueMappingsEditor;
|
const instance = wrapper.instance() as LegacyValueMappingsEditor;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
instance,
|
instance,
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
import React, { PureComponent } from 'react';
|
import React, { PureComponent } from 'react';
|
||||||
|
|
||||||
import MappingRow from './MappingRow';
|
import LegacyMappingRow from './LegacyMappingRow';
|
||||||
import { MappingType, ValueMapping } from '@grafana/data';
|
import { MappingType, ValueMapping } from '@grafana/data';
|
||||||
import { Button } from '../Button/Button';
|
import { Button } from '../Button/Button';
|
||||||
import { PanelOptionsGroup } from '../PanelOptionsGroup/PanelOptionsGroup';
|
import { PanelOptionsGroup } from '../PanelOptionsGroup/PanelOptionsGroup';
|
||||||
@@ -15,7 +15,7 @@ interface State {
|
|||||||
nextIdToAdd: number;
|
nextIdToAdd: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class ValueMappingsEditor extends PureComponent<Props, State> {
|
export class LegacyValueMappingsEditor extends PureComponent<Props, State> {
|
||||||
constructor(props: Props) {
|
constructor(props: Props) {
|
||||||
super(props);
|
super(props);
|
||||||
|
|
||||||
@@ -91,7 +91,7 @@ export class ValueMappingsEditor extends PureComponent<Props, State> {
|
|||||||
<div>
|
<div>
|
||||||
{valueMappings.length > 0 &&
|
{valueMappings.length > 0 &&
|
||||||
valueMappings.map((valueMapping, index) => (
|
valueMappings.map((valueMapping, index) => (
|
||||||
<MappingRow
|
<LegacyMappingRow
|
||||||
key={`${valueMapping.text}-${index}`}
|
key={`${valueMapping.text}-${index}`}
|
||||||
valueMapping={valueMapping}
|
valueMapping={valueMapping}
|
||||||
updateValueMapping={this.updateGauge}
|
updateValueMapping={this.updateGauge}
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
import { storiesOf } from '@storybook/react';
|
|
||||||
import { action } from '@storybook/addon-actions';
|
|
||||||
import { ValueMappingsEditor } from './ValueMappingsEditor';
|
|
||||||
|
|
||||||
const ValueMappingsEditorStories = storiesOf('Panel/ValueMappingsEditor', module);
|
|
||||||
|
|
||||||
ValueMappingsEditorStories.add('default', () => {
|
|
||||||
return <ValueMappingsEditor valueMappings={[]} onChange={action('Mapping changed')} />;
|
|
||||||
});
|
|
||||||
@@ -5,7 +5,7 @@ exports[`Render should render component 1`] = `
|
|||||||
title="Value mappings"
|
title="Value mappings"
|
||||||
>
|
>
|
||||||
<div>
|
<div>
|
||||||
<MappingRow
|
<LegacyMappingRow
|
||||||
key="Ok-0"
|
key="Ok-0"
|
||||||
removeValueMapping={[Function]}
|
removeValueMapping={[Function]}
|
||||||
updateValueMapping={[Function]}
|
updateValueMapping={[Function]}
|
||||||
@@ -19,7 +19,7 @@ exports[`Render should render component 1`] = `
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
<MappingRow
|
<LegacyMappingRow
|
||||||
key="Meh-1"
|
key="Meh-1"
|
||||||
removeValueMapping={[Function]}
|
removeValueMapping={[Function]}
|
||||||
updateValueMapping={[Function]}
|
updateValueMapping={[Function]}
|
||||||
@@ -28,7 +28,7 @@ export { ColorPicker, SeriesColorPicker } from './ColorPicker/ColorPicker';
|
|||||||
export { SeriesColorPickerPopover, SeriesColorPickerPopoverWithTheme } from './ColorPicker/SeriesColorPickerPopover';
|
export { SeriesColorPickerPopover, SeriesColorPickerPopoverWithTheme } from './ColorPicker/SeriesColorPickerPopover';
|
||||||
export { PanelOptionsGroup } from './PanelOptionsGroup/PanelOptionsGroup';
|
export { PanelOptionsGroup } from './PanelOptionsGroup/PanelOptionsGroup';
|
||||||
export { PanelOptionsGrid } from './PanelOptionsGrid/PanelOptionsGrid';
|
export { PanelOptionsGrid } from './PanelOptionsGrid/PanelOptionsGrid';
|
||||||
export { ValueMappingsEditor } from './ValueMappingsEditor/ValueMappingsEditor';
|
export { LegacyValueMappingsEditor } from './ValueMappingsEditor/LegacyValueMappingsEditor';
|
||||||
export { Switch } from './Switch/Switch';
|
export { Switch } from './Switch/Switch';
|
||||||
export { EmptySearchResult } from './EmptySearchResult/EmptySearchResult';
|
export { EmptySearchResult } from './EmptySearchResult/EmptySearchResult';
|
||||||
export { PieChart, PieChartType } from './PieChart/PieChart';
|
export { PieChart, PieChartType } from './PieChart/PieChart';
|
||||||
|
|||||||
@@ -89,33 +89,25 @@ export class PanelEditorUnconnected extends PureComponent<Props> {
|
|||||||
this.props.updateLocation({ query: { tab: tab.id }, partial: true });
|
this.props.updateLocation({ query: { tab: tab.id }, partial: true });
|
||||||
};
|
};
|
||||||
|
|
||||||
onFieldConfigsChange = (fieldOptions: FieldConfigSource) => {
|
onFieldConfigChange = (config: FieldConfigSource) => {
|
||||||
// NOTE: for now, assume this is from 'fieldOptions' -- TODO? put on panel model directly?
|
|
||||||
const { panel } = this.props;
|
const { panel } = this.props;
|
||||||
const options = panel.getOptions();
|
|
||||||
panel.updateOptions({
|
panel.updateFieldConfig({
|
||||||
...options,
|
...config,
|
||||||
fieldOptions, // Assume it is from shared singlestat -- TODO own property?
|
|
||||||
});
|
});
|
||||||
this.forceUpdate();
|
this.forceUpdate();
|
||||||
};
|
};
|
||||||
|
|
||||||
renderFieldOptions(plugin: PanelPlugin) {
|
renderFieldOptions(plugin: PanelPlugin) {
|
||||||
const { panel, data } = this.props;
|
const { panel, data } = this.props;
|
||||||
|
const { fieldConfig } = panel;
|
||||||
|
|
||||||
const fieldOptions = panel.options['fieldOptions'] as FieldConfigSource;
|
if (!fieldConfig) {
|
||||||
|
|
||||||
if (!fieldOptions) {
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<FieldConfigEditor
|
<FieldConfigEditor config={fieldConfig} plugin={plugin} onChange={this.onFieldConfigChange} data={data.series} />
|
||||||
config={fieldOptions}
|
|
||||||
plugin={plugin}
|
|
||||||
onChange={this.onFieldConfigsChange}
|
|
||||||
data={data.series}
|
|
||||||
/>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -130,7 +122,13 @@ export class PanelEditorUnconnected extends PureComponent<Props> {
|
|||||||
if (plugin.editor && panel) {
|
if (plugin.editor && panel) {
|
||||||
return (
|
return (
|
||||||
<div style={{ marginTop: '10px' }}>
|
<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>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -89,7 +89,7 @@ describe('ShareModal', () => {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
ctx.mount({
|
ctx.mount({
|
||||||
panel: { id: 22, options: {} },
|
panel: { id: 22, options: {}, fieldConfig: { defaults: {}, overrides: [] } },
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -101,7 +101,7 @@ describe('ShareModal', () => {
|
|||||||
it('should generate render url', () => {
|
it('should generate render url', () => {
|
||||||
mockLocationHref('http://dashboards.grafana.com/d/abcdefghi/my-dash');
|
mockLocationHref('http://dashboards.grafana.com/d/abcdefghi/my-dash');
|
||||||
ctx.mount({
|
ctx.mount({
|
||||||
panel: { id: 22, options: {} },
|
panel: { id: 22, options: {}, fieldConfig: { defaults: {}, overrides: [] } },
|
||||||
});
|
});
|
||||||
|
|
||||||
const state = ctx.wrapper?.state();
|
const state = ctx.wrapper?.state();
|
||||||
@@ -113,7 +113,7 @@ describe('ShareModal', () => {
|
|||||||
it('should generate render url for scripted dashboard', () => {
|
it('should generate render url for scripted dashboard', () => {
|
||||||
mockLocationHref('http://dashboards.grafana.com/dashboard/script/my-dash.js');
|
mockLocationHref('http://dashboards.grafana.com/dashboard/script/my-dash.js');
|
||||||
ctx.mount({
|
ctx.mount({
|
||||||
panel: { id: 22, options: {} },
|
panel: { id: 22, options: {}, fieldConfig: { defaults: {}, overrides: [] } },
|
||||||
});
|
});
|
||||||
|
|
||||||
const state = ctx.wrapper?.state();
|
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', () => {
|
it('should remove fullscreen from image url when is first param in querystring and modeSharePanel is true', () => {
|
||||||
mockLocationHref('http://server/#!/test?fullscreen&edit');
|
mockLocationHref('http://server/#!/test?fullscreen&edit');
|
||||||
ctx.mount({
|
ctx.mount({
|
||||||
panel: { id: 1, options: {} },
|
panel: { id: 1, options: {}, fieldConfig: { defaults: {}, overrides: [] } },
|
||||||
});
|
});
|
||||||
|
|
||||||
const state = ctx.wrapper?.state();
|
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', () => {
|
it('should remove edit from image url when is first param in querystring and modeSharePanel is true', () => {
|
||||||
mockLocationHref('http://server/#!/test?edit&fullscreen');
|
mockLocationHref('http://server/#!/test?edit&fullscreen');
|
||||||
ctx.mount({
|
ctx.mount({
|
||||||
panel: { id: 1, options: {} },
|
panel: { id: 1, options: {}, fieldConfig: { defaults: {}, overrides: [] } },
|
||||||
});
|
});
|
||||||
|
|
||||||
const state = ctx.wrapper?.state();
|
const state = ctx.wrapper?.state();
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ import {
|
|||||||
PanelEvents,
|
PanelEvents,
|
||||||
PanelData,
|
PanelData,
|
||||||
PanelPlugin,
|
PanelPlugin,
|
||||||
|
FieldConfigSource,
|
||||||
} from '@grafana/data';
|
} from '@grafana/data';
|
||||||
|
|
||||||
const DEFAULT_PLUGIN_ERROR = 'Error in plugin';
|
const DEFAULT_PLUGIN_ERROR = 'Error in plugin';
|
||||||
@@ -217,6 +218,10 @@ export class PanelChrome extends PureComponent<Props, State> {
|
|||||||
this.props.panel.updateOptions(options);
|
this.props.panel.updateOptions(options);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
onFieldConfigChange = (config: FieldConfigSource) => {
|
||||||
|
this.props.panel.updateFieldConfig(config);
|
||||||
|
};
|
||||||
|
|
||||||
onPanelError = (message: string) => {
|
onPanelError = (message: string) => {
|
||||||
if (this.state.errorMessage !== message) {
|
if (this.state.errorMessage !== message) {
|
||||||
this.setState({ errorMessage: message });
|
this.setState({ errorMessage: message });
|
||||||
@@ -281,12 +286,14 @@ export class PanelChrome extends PureComponent<Props, State> {
|
|||||||
timeRange={timeRange}
|
timeRange={timeRange}
|
||||||
timeZone={this.props.dashboard.getTimezone()}
|
timeZone={this.props.dashboard.getTimezone()}
|
||||||
options={panelOptions}
|
options={panelOptions}
|
||||||
|
fieldConfig={panel.fieldConfig}
|
||||||
transparent={panel.transparent}
|
transparent={panel.transparent}
|
||||||
width={panelWidth}
|
width={panelWidth}
|
||||||
height={innerPanelHeight}
|
height={innerPanelHeight}
|
||||||
renderCounter={renderCounter}
|
renderCounter={renderCounter}
|
||||||
replaceVariables={panel.replaceVariables}
|
replaceVariables={panel.replaceVariables}
|
||||||
onOptionsChange={this.onOptionsChange}
|
onOptionsChange={this.onOptionsChange}
|
||||||
|
onFieldConfigChange={this.onFieldConfigChange}
|
||||||
onChangeTimeRange={this.onChangeTimeRange}
|
onChangeTimeRange={this.onChangeTimeRange}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -15,7 +15,14 @@ import { PanelModel, DashboardModel } from '../state';
|
|||||||
import { VizPickerSearch } from './VizPickerSearch';
|
import { VizPickerSearch } from './VizPickerSearch';
|
||||||
import PluginStateinfo from 'app/features/plugins/PluginStateInfo';
|
import PluginStateinfo from 'app/features/plugins/PluginStateInfo';
|
||||||
import { Unsubscribable } from 'rxjs';
|
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 {
|
interface Props {
|
||||||
panel: PanelModel;
|
panel: PanelModel;
|
||||||
@@ -59,6 +66,11 @@ export class VisualizationTab extends PureComponent<Props, State> {
|
|||||||
return panel.getOptions();
|
return panel.getOptions();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
getReactPanelFieldConfig = () => {
|
||||||
|
const { panel } = this.props;
|
||||||
|
return panel.getFieldConfig();
|
||||||
|
};
|
||||||
|
|
||||||
renderPanelOptions() {
|
renderPanelOptions() {
|
||||||
const { plugin, dashboard, panel } = this.props;
|
const { plugin, dashboard, panel } = this.props;
|
||||||
|
|
||||||
@@ -72,6 +84,10 @@ export class VisualizationTab extends PureComponent<Props, State> {
|
|||||||
data={this.state.data}
|
data={this.state.data}
|
||||||
options={this.getReactPanelOptions()}
|
options={this.getReactPanelOptions()}
|
||||||
onOptionsChange={this.onPanelOptionsChanged}
|
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);
|
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 = () => {
|
onOpenVizPicker = () => {
|
||||||
this.setState({ isVizPickerOpen: true, scrollTop: 0 });
|
this.setState({ isVizPickerOpen: true, scrollTop: 0 });
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { PanelModel } from './PanelModel';
|
import { PanelModel } from './PanelModel';
|
||||||
import { getPanelPlugin } from '../../plugins/__mocks__/pluginMocks';
|
import { getPanelPlugin } from '../../plugins/__mocks__/pluginMocks';
|
||||||
import { PanelProps } from '@grafana/data';
|
import { ConfigOverrideRule, PanelProps } from '@grafana/data';
|
||||||
import { ComponentClass } from 'react';
|
import { ComponentClass } from 'react';
|
||||||
|
|
||||||
class TablePanelCtrl {}
|
class TablePanelCtrl {}
|
||||||
@@ -53,9 +53,31 @@ describe('PanelModel', () => {
|
|||||||
showColumns: true,
|
showColumns: true,
|
||||||
targets: [{ refId: 'A' }, { noRefId: true }],
|
targets: [{ refId: 'A' }, { noRefId: true }],
|
||||||
options: persistedOptionsMock,
|
options: persistedOptionsMock,
|
||||||
|
fieldConfig: {
|
||||||
|
defaults: {
|
||||||
|
unit: 'mpg',
|
||||||
|
},
|
||||||
|
overrides: [
|
||||||
|
{
|
||||||
|
matcher: {
|
||||||
|
id: '1',
|
||||||
|
options: {},
|
||||||
|
},
|
||||||
|
properties: [],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
model = new PanelModel(modelJson);
|
model = new PanelModel(modelJson);
|
||||||
|
const overrideMock: ConfigOverrideRule = {
|
||||||
|
matcher: {
|
||||||
|
id: '2',
|
||||||
|
options: {},
|
||||||
|
},
|
||||||
|
properties: [],
|
||||||
|
};
|
||||||
|
|
||||||
const panelPlugin = getPanelPlugin(
|
const panelPlugin = getPanelPlugin(
|
||||||
{
|
{
|
||||||
id: 'table',
|
id: 'table',
|
||||||
@@ -64,6 +86,13 @@ describe('PanelModel', () => {
|
|||||||
TablePanelCtrl // angular
|
TablePanelCtrl // angular
|
||||||
);
|
);
|
||||||
panelPlugin.setDefaults(defaultOptionsMock);
|
panelPlugin.setDefaults(defaultOptionsMock);
|
||||||
|
panelPlugin.setFieldConfigDefaults({
|
||||||
|
defaults: {
|
||||||
|
unit: 'flop',
|
||||||
|
decimals: 2,
|
||||||
|
},
|
||||||
|
overrides: [overrideMock],
|
||||||
|
});
|
||||||
model.pluginLoaded(panelPlugin);
|
model.pluginLoaded(panelPlugin);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -79,6 +108,17 @@ describe('PanelModel', () => {
|
|||||||
expect(model.getOptions().arrayWith2Values.length).toBe(1);
|
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', () => {
|
it('should set model props on instance', () => {
|
||||||
expect(model.showColumns).toBe(true);
|
expect(model.showColumns).toBe(true);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import {
|
|||||||
PanelEvents,
|
PanelEvents,
|
||||||
PanelPlugin,
|
PanelPlugin,
|
||||||
ScopedVars,
|
ScopedVars,
|
||||||
|
FieldConfigSource,
|
||||||
} from '@grafana/data';
|
} from '@grafana/data';
|
||||||
import { EDIT_PANEL_ID } from 'app/core/constants';
|
import { EDIT_PANEL_ID } from 'app/core/constants';
|
||||||
|
|
||||||
@@ -81,6 +82,7 @@ const mustKeepProps: { [str: string]: boolean } = {
|
|||||||
pluginVersion: true,
|
pluginVersion: true,
|
||||||
queryRunner: true,
|
queryRunner: true,
|
||||||
transformations: true,
|
transformations: true,
|
||||||
|
fieldConfig: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
const defaults: any = {
|
const defaults: any = {
|
||||||
@@ -121,6 +123,7 @@ export class PanelModel implements DataConfigSource {
|
|||||||
options: {
|
options: {
|
||||||
[key: string]: any;
|
[key: string]: any;
|
||||||
};
|
};
|
||||||
|
fieldConfig: FieldConfigSource;
|
||||||
|
|
||||||
maxDataPoints?: number;
|
maxDataPoints?: number;
|
||||||
interval?: string;
|
interval?: string;
|
||||||
@@ -177,9 +180,19 @@ export class PanelModel implements DataConfigSource {
|
|||||||
getOptions() {
|
getOptions() {
|
||||||
return this.options;
|
return this.options;
|
||||||
}
|
}
|
||||||
|
getFieldConfig() {
|
||||||
|
return this.fieldConfig;
|
||||||
|
}
|
||||||
|
|
||||||
updateOptions(options: object) {
|
updateOptions(options: object) {
|
||||||
this.options = options;
|
this.options = options;
|
||||||
|
|
||||||
|
this.render();
|
||||||
|
}
|
||||||
|
|
||||||
|
updateFieldConfig(config: FieldConfigSource) {
|
||||||
|
this.fieldConfig = config;
|
||||||
|
|
||||||
this.resendLastResult();
|
this.resendLastResult();
|
||||||
this.render();
|
this.render();
|
||||||
}
|
}
|
||||||
@@ -273,6 +286,23 @@ export class PanelModel implements DataConfigSource {
|
|||||||
return srcValue;
|
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) {
|
pluginLoaded(plugin: PanelPlugin) {
|
||||||
@@ -382,7 +412,7 @@ export class PanelModel implements DataConfigSource {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
fieldOptions: this.options.fieldOptions,
|
fieldOptions: this.fieldConfig,
|
||||||
replaceVariables: this.replaceVariables,
|
replaceVariables: this.replaceVariables,
|
||||||
custom: this.plugin.customFieldConfigs,
|
custom: this.plugin.customFieldConfigs,
|
||||||
theme: config.theme,
|
theme: config.theme,
|
||||||
|
|||||||
@@ -43,8 +43,43 @@ describe('BarGauge Panel Migrations', () => {
|
|||||||
targets: [],
|
targets: [],
|
||||||
title: 'Usage',
|
title: 'Usage',
|
||||||
type: 'bargauge',
|
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,
|
PanelProps,
|
||||||
LoadingState,
|
LoadingState,
|
||||||
dateTime,
|
dateTime,
|
||||||
|
FieldConfigSource,
|
||||||
toDataFrame,
|
toDataFrame,
|
||||||
} from '@grafana/data';
|
} from '@grafana/data';
|
||||||
import { BarGaugeDisplayMode } from '@grafana/ui';
|
import { BarGaugeDisplayMode } from '@grafana/ui';
|
||||||
@@ -66,13 +67,15 @@ function createBarGaugePanelWithData(data: PanelData): ReactWrapper<PanelProps<B
|
|||||||
displayMode: BarGaugeDisplayMode.Lcd,
|
displayMode: BarGaugeDisplayMode.Lcd,
|
||||||
fieldOptions: {
|
fieldOptions: {
|
||||||
calcs: ['mean'],
|
calcs: ['mean'],
|
||||||
defaults: {},
|
|
||||||
values: false,
|
values: false,
|
||||||
overrides: [],
|
|
||||||
},
|
},
|
||||||
orientation: VizOrientation.Horizontal,
|
orientation: VizOrientation.Horizontal,
|
||||||
showUnfilled: true,
|
showUnfilled: true,
|
||||||
};
|
};
|
||||||
|
const fieldConfig: FieldConfigSource = {
|
||||||
|
defaults: {},
|
||||||
|
overrides: [],
|
||||||
|
};
|
||||||
|
|
||||||
return mount<BarGaugePanel>(
|
return mount<BarGaugePanel>(
|
||||||
<BarGaugePanel
|
<BarGaugePanel
|
||||||
@@ -81,6 +84,8 @@ function createBarGaugePanelWithData(data: PanelData): ReactWrapper<PanelProps<B
|
|||||||
timeRange={timeRange}
|
timeRange={timeRange}
|
||||||
timeZone={'utc'}
|
timeZone={'utc'}
|
||||||
options={options}
|
options={options}
|
||||||
|
fieldConfig={fieldConfig}
|
||||||
|
onFieldConfigChange={() => {}}
|
||||||
onOptionsChange={() => {}}
|
onOptionsChange={() => {}}
|
||||||
onChangeTimeRange={() => {}}
|
onChangeTimeRange={() => {}}
|
||||||
replaceVariables={s => s}
|
replaceVariables={s => s}
|
||||||
|
|||||||
@@ -51,9 +51,10 @@ export class BarGaugePanel extends PureComponent<PanelProps<BarGaugeOptions>> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
getValues = (): FieldDisplay[] => {
|
getValues = (): FieldDisplay[] => {
|
||||||
const { data, options, replaceVariables } = this.props;
|
const { data, options, replaceVariables, fieldConfig } = this.props;
|
||||||
return getFieldDisplayValues({
|
return getFieldDisplayValues({
|
||||||
...options,
|
fieldConfig,
|
||||||
|
fieldOptions: options.fieldOptions,
|
||||||
replaceVariables,
|
replaceVariables,
|
||||||
theme: config.theme,
|
theme: config.theme,
|
||||||
data: data.series,
|
data: data.series,
|
||||||
|
|||||||
@@ -2,83 +2,94 @@
|
|||||||
import React, { PureComponent } from 'react';
|
import React, { PureComponent } from 'react';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
ThresholdsEditor,
|
|
||||||
ValueMappingsEditor,
|
|
||||||
PanelOptionsGrid,
|
PanelOptionsGrid,
|
||||||
FieldDisplayEditor,
|
FieldDisplayEditor,
|
||||||
FieldPropertiesEditor,
|
|
||||||
PanelOptionsGroup,
|
PanelOptionsGroup,
|
||||||
FormLabel,
|
FormLabel,
|
||||||
Select,
|
Select,
|
||||||
DataLinksEditor,
|
|
||||||
Switch,
|
Switch,
|
||||||
|
FieldPropertiesEditor,
|
||||||
|
ThresholdsEditor,
|
||||||
|
LegacyValueMappingsEditor,
|
||||||
|
DataLinksEditor,
|
||||||
} from '@grafana/ui';
|
} from '@grafana/ui';
|
||||||
import {
|
import {
|
||||||
|
DataLink,
|
||||||
|
FieldConfig,
|
||||||
|
FieldDisplayOptions,
|
||||||
|
PanelEditorProps,
|
||||||
ThresholdsConfig,
|
ThresholdsConfig,
|
||||||
ValueMapping,
|
ValueMapping,
|
||||||
FieldDisplayOptions,
|
|
||||||
FieldConfig,
|
|
||||||
DataLink,
|
|
||||||
PanelEditorProps,
|
|
||||||
} from '@grafana/data';
|
} from '@grafana/data';
|
||||||
import { BarGaugeOptions, displayModes } from './types';
|
import { BarGaugeOptions, displayModes } from './types';
|
||||||
import { orientationOptions } from '../gauge/types';
|
import { orientationOptions } from '../gauge/types';
|
||||||
import {
|
import {
|
||||||
getDataLinksVariableSuggestions,
|
|
||||||
getCalculationValueDataLinksVariableSuggestions,
|
getCalculationValueDataLinksVariableSuggestions,
|
||||||
} from 'app/features/panel/panellinks/link_srv';
|
getDataLinksVariableSuggestions,
|
||||||
|
} from '../../../features/panel/panellinks/link_srv';
|
||||||
|
|
||||||
export class BarGaugePanelEditor extends PureComponent<PanelEditorProps<BarGaugeOptions>> {
|
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) =>
|
onDisplayOptionsChanged = (fieldOptions: FieldDisplayOptions) =>
|
||||||
this.props.onOptionsChange({
|
this.props.onOptionsChange({
|
||||||
...this.props.options,
|
...this.props.options,
|
||||||
fieldOptions,
|
fieldOptions,
|
||||||
});
|
});
|
||||||
|
|
||||||
onDefaultsChange = (field: FieldConfig) => {
|
|
||||||
this.onDisplayOptionsChanged({
|
|
||||||
...this.props.options.fieldOptions,
|
|
||||||
defaults: field,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
onOrientationChange = ({ value }: any) => this.props.onOptionsChange({ ...this.props.options, orientation: value });
|
onOrientationChange = ({ value }: any) => this.props.onOptionsChange({ ...this.props.options, orientation: value });
|
||||||
onDisplayModeChange = ({ value }: any) => this.props.onOptionsChange({ ...this.props.options, displayMode: value });
|
onDisplayModeChange = ({ value }: any) => this.props.onOptionsChange({ ...this.props.options, displayMode: value });
|
||||||
onToggleShowUnfilled = () => {
|
onToggleShowUnfilled = () => {
|
||||||
this.props.onOptionsChange({ ...this.props.options, showUnfilled: !this.props.options.showUnfilled });
|
this.props.onOptionsChange({ ...this.props.options, showUnfilled: !this.props.options.showUnfilled });
|
||||||
};
|
};
|
||||||
|
|
||||||
onDataLinksChanged = (links: DataLink[]) => {
|
onThresholdsChanged = (thresholds: ThresholdsConfig) => {
|
||||||
this.onDefaultsChange({
|
const current = this.props.fieldConfig;
|
||||||
...this.props.options.fieldOptions.defaults,
|
this.props.onFieldConfigChange({
|
||||||
links,
|
...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
|
const suggestions = fieldOptions.values
|
||||||
? getDataLinksVariableSuggestions(this.props.data.series)
|
? getDataLinksVariableSuggestions(this.props.data.series)
|
||||||
: getCalculationValueDataLinksVariableSuggestions(this.props.data.series);
|
: getCalculationValueDataLinksVariableSuggestions(this.props.data.series);
|
||||||
const labelWidth = 6;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
@@ -105,14 +116,16 @@ export class BarGaugePanelEditor extends PureComponent<PanelEditorProps<BarGauge
|
|||||||
value={displayModes.find(item => item.value === options.displayMode)}
|
value={displayModes.find(item => item.value === options.displayMode)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
{options.displayMode !== 'lcd' && (
|
<>
|
||||||
<Switch
|
{options.displayMode !== 'lcd' && (
|
||||||
label="Unfilled"
|
<Switch
|
||||||
labelClass={`width-${labelWidth}`}
|
label="Unfilled"
|
||||||
checked={options.showUnfilled}
|
labelClass={`width-${labelWidth}`}
|
||||||
onChange={this.onToggleShowUnfilled}
|
checked={options.showUnfilled}
|
||||||
/>
|
onChange={this.onToggleShowUnfilled}
|
||||||
)}
|
/>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
</PanelOptionsGroup>
|
</PanelOptionsGroup>
|
||||||
<PanelOptionsGroup title="Field">
|
<PanelOptionsGroup title="Field">
|
||||||
<FieldPropertiesEditor
|
<FieldPropertiesEditor
|
||||||
@@ -126,7 +139,7 @@ export class BarGaugePanelEditor extends PureComponent<PanelEditorProps<BarGauge
|
|||||||
<ThresholdsEditor onChange={this.onThresholdsChanged} thresholds={defaults.thresholds} />
|
<ThresholdsEditor onChange={this.onThresholdsChanged} thresholds={defaults.thresholds} />
|
||||||
</PanelOptionsGrid>
|
</PanelOptionsGrid>
|
||||||
|
|
||||||
<ValueMappingsEditor onChange={this.onValueMappingsChanged} valueMappings={defaults.mappings} />
|
<LegacyValueMappingsEditor onChange={this.onValueMappingsChanged} valueMappings={defaults.mappings} />
|
||||||
|
|
||||||
<PanelOptionsGroup title="Data links">
|
<PanelOptionsGroup title="Data links">
|
||||||
<DataLinksEditor
|
<DataLinksEditor
|
||||||
|
|||||||
@@ -7,37 +7,6 @@ Object {
|
|||||||
"calcs": Array [
|
"calcs": Array [
|
||||||
"mean",
|
"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 [
|
"thresholds": Array [
|
||||||
Object {
|
Object {
|
||||||
"color": "green",
|
"color": "green",
|
||||||
|
|||||||
@@ -3,10 +3,12 @@ import { PanelPlugin } from '@grafana/data';
|
|||||||
import { BarGaugePanel } from './BarGaugePanel';
|
import { BarGaugePanel } from './BarGaugePanel';
|
||||||
import { BarGaugePanelEditor } from './BarGaugePanelEditor';
|
import { BarGaugePanelEditor } from './BarGaugePanelEditor';
|
||||||
import { BarGaugeOptions, defaults } from './types';
|
import { BarGaugeOptions, defaults } from './types';
|
||||||
|
import { standardFieldConfig } from '../stat/types';
|
||||||
import { barGaugePanelMigrationHandler } from './BarGaugeMigrations';
|
import { barGaugePanelMigrationHandler } from './BarGaugeMigrations';
|
||||||
|
|
||||||
export const plugin = new PanelPlugin<BarGaugeOptions>(BarGaugePanel)
|
export const plugin = new PanelPlugin<BarGaugeOptions>(BarGaugePanel)
|
||||||
.setDefaults(defaults)
|
.setDefaults(defaults)
|
||||||
|
.setFieldConfigDefaults(standardFieldConfig)
|
||||||
.setEditor(BarGaugePanelEditor)
|
.setEditor(BarGaugePanelEditor)
|
||||||
.setPanelChangeHandler(sharedSingleStatPanelChangedHandler)
|
.setPanelChangeHandler(sharedSingleStatPanelChangedHandler)
|
||||||
.setMigrationHandler(barGaugePanelMigrationHandler);
|
.setMigrationHandler(barGaugePanelMigrationHandler);
|
||||||
|
|||||||
@@ -75,9 +75,68 @@ describe('Gauge Panel Migrations', () => {
|
|||||||
timeShift: null,
|
timeShift: null,
|
||||||
title: 'Panel Title',
|
title: 'Panel Title',
|
||||||
type: 'gauge',
|
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', () => {
|
it('change from angular singlestat to gauge', () => {
|
||||||
@@ -95,11 +154,12 @@ describe('Gauge Panel Migrations', () => {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const newOptions = gaugePanelChangedHandler({} as any, 'singlestat', old);
|
const panel = {} as PanelModel;
|
||||||
expect(newOptions.fieldOptions.defaults.unit).toBe('ms');
|
const newOptions = gaugePanelChangedHandler(panel, 'singlestat', old);
|
||||||
expect(newOptions.fieldOptions.defaults.min).toBe(-10);
|
expect(panel.fieldConfig.defaults.unit).toBe('ms');
|
||||||
expect(newOptions.fieldOptions.defaults.max).toBe(150);
|
expect(panel.fieldConfig.defaults.min).toBe(-10);
|
||||||
expect(newOptions.fieldOptions.defaults.decimals).toBe(7);
|
expect(panel.fieldConfig.defaults.max).toBe(150);
|
||||||
|
expect(panel.fieldConfig.defaults.decimals).toBe(7);
|
||||||
expect(newOptions.showThresholdMarkers).toBe(true);
|
expect(newOptions.showThresholdMarkers).toBe(true);
|
||||||
expect(newOptions.showThresholdLabels).toBe(true);
|
expect(newOptions.showThresholdLabels).toBe(true);
|
||||||
});
|
});
|
||||||
@@ -116,10 +176,10 @@ describe('Gauge Panel Migrations', () => {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
const panel = {} as PanelModel;
|
||||||
const newOptions = gaugePanelChangedHandler({} as any, 'singlestat', old);
|
gaugePanelChangedHandler(panel, 'singlestat', old);
|
||||||
expect(newOptions.fieldOptions.defaults.unit).toBe('ms');
|
expect(panel.fieldConfig.defaults.unit).toBe('ms');
|
||||||
expect(newOptions.fieldOptions.defaults.min).toBe(undefined);
|
expect(panel.fieldConfig.defaults.min).toBe(undefined);
|
||||||
expect(newOptions.fieldOptions.defaults.max).toBe(undefined);
|
expect(panel.fieldConfig.defaults.max).toBe(undefined);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -40,8 +40,9 @@ export class GaugePanel extends PureComponent<PanelProps<GaugeOptions>> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
getValues = (): FieldDisplay[] => {
|
getValues = (): FieldDisplay[] => {
|
||||||
const { data, options, replaceVariables } = this.props;
|
const { data, options, replaceVariables, fieldConfig } = this.props;
|
||||||
return getFieldDisplayValues({
|
return getFieldDisplayValues({
|
||||||
|
fieldConfig,
|
||||||
fieldOptions: options.fieldOptions,
|
fieldOptions: options.fieldOptions,
|
||||||
replaceVariables,
|
replaceVariables,
|
||||||
theme: config.theme,
|
theme: config.theme,
|
||||||
|
|||||||
@@ -1,29 +1,29 @@
|
|||||||
// Libraries
|
// Libraries
|
||||||
import React, { PureComponent } from 'react';
|
import React, { PureComponent } from 'react';
|
||||||
import {
|
import {
|
||||||
ThresholdsEditor,
|
|
||||||
PanelOptionsGrid,
|
PanelOptionsGrid,
|
||||||
ValueMappingsEditor,
|
|
||||||
FieldDisplayEditor,
|
FieldDisplayEditor,
|
||||||
FieldPropertiesEditor,
|
|
||||||
Switch,
|
Switch,
|
||||||
PanelOptionsGroup,
|
PanelOptionsGroup,
|
||||||
|
FieldPropertiesEditor,
|
||||||
|
ThresholdsEditor,
|
||||||
|
LegacyValueMappingsEditor,
|
||||||
DataLinksEditor,
|
DataLinksEditor,
|
||||||
} from '@grafana/ui';
|
} from '@grafana/ui';
|
||||||
import {
|
import {
|
||||||
PanelEditorProps,
|
PanelEditorProps,
|
||||||
FieldDisplayOptions,
|
FieldDisplayOptions,
|
||||||
ThresholdsConfig,
|
ThresholdsConfig,
|
||||||
ValueMapping,
|
|
||||||
FieldConfig,
|
|
||||||
DataLink,
|
DataLink,
|
||||||
|
FieldConfig,
|
||||||
|
ValueMapping,
|
||||||
} from '@grafana/data';
|
} from '@grafana/data';
|
||||||
|
|
||||||
import { GaugeOptions } from './types';
|
import { GaugeOptions } from './types';
|
||||||
import {
|
import {
|
||||||
getCalculationValueDataLinksVariableSuggestions,
|
getCalculationValueDataLinksVariableSuggestions,
|
||||||
getDataLinksVariableSuggestions,
|
getDataLinksVariableSuggestions,
|
||||||
} from 'app/features/panel/panellinks/link_srv';
|
} from '../../../features/panel/panellinks/link_srv';
|
||||||
|
|
||||||
export class GaugePanelEditor extends PureComponent<PanelEditorProps<GaugeOptions>> {
|
export class GaugePanelEditor extends PureComponent<PanelEditorProps<GaugeOptions>> {
|
||||||
labelWidth = 6;
|
labelWidth = 6;
|
||||||
@@ -37,27 +37,11 @@ export class GaugePanelEditor extends PureComponent<PanelEditorProps<GaugeOption
|
|||||||
showThresholdMarkers: !this.props.options.showThresholdMarkers,
|
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 = (
|
onDisplayOptionsChanged = (
|
||||||
fieldOptions: FieldDisplayOptions,
|
fieldOptions: FieldDisplayOptions,
|
||||||
event?: React.SyntheticEvent<HTMLElement>,
|
event?: React.SyntheticEvent<HTMLElement>,
|
||||||
callback?: () => void
|
callback?: () => void
|
||||||
) =>
|
) => {
|
||||||
this.props.onOptionsChange(
|
this.props.onOptionsChange(
|
||||||
{
|
{
|
||||||
...this.props.options,
|
...this.props.options,
|
||||||
@@ -65,38 +49,57 @@ export class GaugePanelEditor extends PureComponent<PanelEditorProps<GaugeOption
|
|||||||
},
|
},
|
||||||
callback
|
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) => {
|
onThresholdsChanged = (thresholds: ThresholdsConfig) => {
|
||||||
this.onDefaultsChange(
|
const current = this.props.fieldConfig;
|
||||||
{
|
this.props.onFieldConfigChange({
|
||||||
...this.props.options.fieldOptions.defaults,
|
...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,
|
links,
|
||||||
},
|
},
|
||||||
undefined,
|
});
|
||||||
callback
|
};
|
||||||
);
|
|
||||||
|
onDefaultsChange = (field: FieldConfig) => {
|
||||||
|
this.props.onFieldConfigChange({
|
||||||
|
...this.props.fieldConfig,
|
||||||
|
defaults: field,
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { options } = this.props;
|
const { options, fieldConfig } = this.props;
|
||||||
const { fieldOptions, showThresholdLabels, showThresholdMarkers } = options;
|
const { showThresholdLabels, showThresholdMarkers, fieldOptions } = options;
|
||||||
const { defaults } = fieldOptions;
|
|
||||||
|
const { defaults } = fieldConfig;
|
||||||
|
|
||||||
const suggestions = fieldOptions.values
|
const suggestions = fieldOptions.values
|
||||||
? getDataLinksVariableSuggestions(this.props.data.series)
|
? getDataLinksVariableSuggestions(this.props.data.series)
|
||||||
: getCalculationValueDataLinksVariableSuggestions(this.props.data.series);
|
: getCalculationValueDataLinksVariableSuggestions(this.props.data.series);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<PanelOptionsGrid>
|
<PanelOptionsGrid>
|
||||||
@@ -128,11 +131,9 @@ export class GaugePanelEditor extends PureComponent<PanelEditorProps<GaugeOption
|
|||||||
value={defaults}
|
value={defaults}
|
||||||
/>
|
/>
|
||||||
</PanelOptionsGroup>
|
</PanelOptionsGroup>
|
||||||
|
|
||||||
<ThresholdsEditor onChange={this.onThresholdsChanged} thresholds={defaults.thresholds} />
|
<ThresholdsEditor onChange={this.onThresholdsChanged} thresholds={defaults.thresholds} />
|
||||||
</PanelOptionsGrid>
|
</PanelOptionsGrid>
|
||||||
|
<LegacyValueMappingsEditor onChange={this.onValueMappingsChanged} valueMappings={defaults.mappings} />
|
||||||
<ValueMappingsEditor onChange={this.onValueMappingsChanged} valueMappings={defaults.mappings} />
|
|
||||||
|
|
||||||
<PanelOptionsGroup title="Data links">
|
<PanelOptionsGroup title="Data links">
|
||||||
<DataLinksEditor
|
<DataLinksEditor
|
||||||
|
|||||||
@@ -6,51 +6,6 @@ Object {
|
|||||||
"calcs": Array [
|
"calcs": Array [
|
||||||
"last",
|
"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",
|
"orientation": "auto",
|
||||||
"showThresholdLabels": true,
|
"showThresholdLabels": true,
|
||||||
|
|||||||
@@ -2,10 +2,12 @@ import { PanelPlugin } from '@grafana/data';
|
|||||||
import { GaugePanelEditor } from './GaugePanelEditor';
|
import { GaugePanelEditor } from './GaugePanelEditor';
|
||||||
import { GaugePanel } from './GaugePanel';
|
import { GaugePanel } from './GaugePanel';
|
||||||
import { GaugeOptions, defaults } from './types';
|
import { GaugeOptions, defaults } from './types';
|
||||||
|
import { standardFieldConfig } from '../stat/types';
|
||||||
import { gaugePanelMigrationHandler, gaugePanelChangedHandler } from './GaugeMigrations';
|
import { gaugePanelMigrationHandler, gaugePanelChangedHandler } from './GaugeMigrations';
|
||||||
|
|
||||||
export const plugin = new PanelPlugin<GaugeOptions>(GaugePanel)
|
export const plugin = new PanelPlugin<GaugeOptions>(GaugePanel)
|
||||||
.setDefaults(defaults)
|
.setDefaults(defaults)
|
||||||
|
.setFieldConfigDefaults(standardFieldConfig)
|
||||||
.setEditor(GaugePanelEditor)
|
.setEditor(GaugePanelEditor)
|
||||||
.setPanelChangeHandler(gaugePanelChangedHandler)
|
.setPanelChangeHandler(gaugePanelChangedHandler)
|
||||||
.setMigrationHandler(gaugePanelMigrationHandler);
|
.setMigrationHandler(gaugePanelMigrationHandler);
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ export const GraphPanel: React.FunctionComponent<GraphPanelProps> = ({
|
|||||||
width,
|
width,
|
||||||
height,
|
height,
|
||||||
options,
|
options,
|
||||||
|
fieldConfig,
|
||||||
onOptionsChange,
|
onOptionsChange,
|
||||||
onChangeTimeRange,
|
onChangeTimeRange,
|
||||||
}) => {
|
}) => {
|
||||||
@@ -43,6 +44,7 @@ export const GraphPanel: React.FunctionComponent<GraphPanelProps> = ({
|
|||||||
data={data}
|
data={data}
|
||||||
timeZone={timeZone}
|
timeZone={timeZone}
|
||||||
options={options}
|
options={options}
|
||||||
|
fieldConfig={fieldConfig}
|
||||||
onOptionsChange={onOptionsChange}
|
onOptionsChange={onOptionsChange}
|
||||||
onChangeTimeRange={onChangeTimeRange}
|
onChangeTimeRange={onChangeTimeRange}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { GraphSeriesToggler } from '@grafana/ui';
|
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 { getGraphSeriesModel } from './getGraphSeriesModel';
|
||||||
import { Options, SeriesOptions } from './types';
|
import { Options, SeriesOptions } from './types';
|
||||||
@@ -18,6 +18,7 @@ interface GraphPanelControllerAPI {
|
|||||||
interface GraphPanelControllerProps {
|
interface GraphPanelControllerProps {
|
||||||
children: (api: GraphPanelControllerAPI) => JSX.Element;
|
children: (api: GraphPanelControllerAPI) => JSX.Element;
|
||||||
options: Options;
|
options: Options;
|
||||||
|
fieldConfig: FieldConfigSource;
|
||||||
data: PanelData;
|
data: PanelData;
|
||||||
timeZone: TimeZone;
|
timeZone: TimeZone;
|
||||||
onOptionsChange: (options: Options) => void;
|
onOptionsChange: (options: Options) => void;
|
||||||
@@ -44,7 +45,7 @@ export class GraphPanelController extends React.Component<GraphPanelControllerPr
|
|||||||
props.options.series,
|
props.options.series,
|
||||||
props.options.graph,
|
props.options.graph,
|
||||||
props.options.legend,
|
props.options.legend,
|
||||||
props.options.fieldOptions
|
props.fieldConfig
|
||||||
),
|
),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -58,7 +59,7 @@ export class GraphPanelController extends React.Component<GraphPanelControllerPr
|
|||||||
props.options.series,
|
props.options.series,
|
||||||
props.options.graph,
|
props.options.graph,
|
||||||
props.options.legend,
|
props.options.legend,
|
||||||
props.options.fieldOptions
|
props.fieldConfig
|
||||||
),
|
),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,15 +3,15 @@ import _ from 'lodash';
|
|||||||
import React, { PureComponent } from 'react';
|
import React, { PureComponent } from 'react';
|
||||||
|
|
||||||
// Types
|
// Types
|
||||||
import { PanelEditorProps, FieldConfig } from '@grafana/data';
|
import { FieldConfig, PanelEditorProps } from '@grafana/data';
|
||||||
import {
|
import {
|
||||||
Switch,
|
Switch,
|
||||||
LegendOptions,
|
LegendOptions,
|
||||||
GraphTooltipOptions,
|
GraphTooltipOptions,
|
||||||
PanelOptionsGrid,
|
PanelOptionsGrid,
|
||||||
PanelOptionsGroup,
|
PanelOptionsGroup,
|
||||||
FieldPropertiesEditor,
|
|
||||||
Select,
|
Select,
|
||||||
|
FieldPropertiesEditor,
|
||||||
} from '@grafana/ui';
|
} from '@grafana/ui';
|
||||||
import { Options, GraphOptions } from './types';
|
import { Options, GraphOptions } from './types';
|
||||||
import { GraphLegendEditor } from './GraphLegendEditor';
|
import { GraphLegendEditor } from './GraphLegendEditor';
|
||||||
@@ -47,13 +47,10 @@ export class GraphPanelEditor extends PureComponent<PanelEditorProps<Options>> {
|
|||||||
this.onGraphOptionsChange({ showPoints: !this.props.options.graph.showPoints });
|
this.onGraphOptionsChange({ showPoints: !this.props.options.graph.showPoints });
|
||||||
};
|
};
|
||||||
|
|
||||||
onDefaultsChange = (field: FieldConfig) => {
|
onDefaultsChange = (field: FieldConfig, event?: React.SyntheticEvent<HTMLElement>, callback?: () => void) => {
|
||||||
this.props.onOptionsChange({
|
this.props.onFieldConfigChange({
|
||||||
...this.props.options,
|
...this.props.fieldConfig,
|
||||||
fieldOptions: {
|
defaults: field,
|
||||||
...this.props.options.fieldOptions,
|
|
||||||
defaults: field,
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -76,7 +73,7 @@ export class GraphPanelEditor extends PureComponent<PanelEditorProps<Options>> {
|
|||||||
<FieldPropertiesEditor
|
<FieldPropertiesEditor
|
||||||
showMinMax={false}
|
showMinMax={false}
|
||||||
onChange={this.onDefaultsChange}
|
onChange={this.onDefaultsChange}
|
||||||
value={this.props.options.fieldOptions.defaults}
|
value={this.props.fieldConfig.defaults}
|
||||||
/>
|
/>
|
||||||
</PanelOptionsGroup>
|
</PanelOptionsGroup>
|
||||||
<PanelOptionsGroup title="Tooltip">
|
<PanelOptionsGroup title="Tooltip">
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ import {
|
|||||||
GraphSeriesXY,
|
GraphSeriesXY,
|
||||||
getTimeField,
|
getTimeField,
|
||||||
DataFrame,
|
DataFrame,
|
||||||
FieldDisplayOptions,
|
|
||||||
getSeriesTimeStep,
|
getSeriesTimeStep,
|
||||||
TimeZone,
|
TimeZone,
|
||||||
hasMsResolution,
|
hasMsResolution,
|
||||||
@@ -17,6 +16,7 @@ import {
|
|||||||
DEFAULT_DATE_TIME_FORMAT,
|
DEFAULT_DATE_TIME_FORMAT,
|
||||||
FieldColor,
|
FieldColor,
|
||||||
FieldColorMode,
|
FieldColorMode,
|
||||||
|
FieldConfigSource,
|
||||||
} from '@grafana/data';
|
} from '@grafana/data';
|
||||||
|
|
||||||
import { SeriesOptions, GraphOptions } from './types';
|
import { SeriesOptions, GraphOptions } from './types';
|
||||||
@@ -28,7 +28,7 @@ export const getGraphSeriesModel = (
|
|||||||
seriesOptions: SeriesOptions,
|
seriesOptions: SeriesOptions,
|
||||||
graphOptions: GraphOptions,
|
graphOptions: GraphOptions,
|
||||||
legendOptions: GraphLegendEditorLegendOptions,
|
legendOptions: GraphLegendEditorLegendOptions,
|
||||||
fieldOptions?: FieldDisplayOptions
|
fieldOptions?: FieldConfigSource
|
||||||
) => {
|
) => {
|
||||||
const graphs: GraphSeriesXY[] = [];
|
const graphs: GraphSeriesXY[] = [];
|
||||||
|
|
||||||
|
|||||||
@@ -16,9 +16,10 @@ interface Props extends PanelProps<PieChartOptions> {}
|
|||||||
|
|
||||||
export class PieChartPanel extends PureComponent<Props> {
|
export class PieChartPanel extends PureComponent<Props> {
|
||||||
render() {
|
render() {
|
||||||
const { width, height, options, data, replaceVariables } = this.props;
|
const { width, height, options, data, replaceVariables, fieldConfig } = this.props;
|
||||||
|
|
||||||
const values = getFieldDisplayValues({
|
const values = getFieldDisplayValues({
|
||||||
|
fieldConfig,
|
||||||
fieldOptions: options.fieldOptions,
|
fieldOptions: options.fieldOptions,
|
||||||
data: data.series,
|
data: data.series,
|
||||||
theme: config.theme,
|
theme: config.theme,
|
||||||
|
|||||||
@@ -1,22 +1,25 @@
|
|||||||
import React, { PureComponent } from 'react';
|
import React, { PureComponent } from 'react';
|
||||||
import {
|
import {
|
||||||
PanelOptionsGrid,
|
PanelOptionsGrid,
|
||||||
ValueMappingsEditor,
|
|
||||||
FieldDisplayEditor,
|
FieldDisplayEditor,
|
||||||
FieldPropertiesEditor,
|
|
||||||
PanelOptionsGroup,
|
PanelOptionsGroup,
|
||||||
|
FieldPropertiesEditor,
|
||||||
|
LegacyValueMappingsEditor,
|
||||||
} from '@grafana/ui';
|
} from '@grafana/ui';
|
||||||
import { ValueMapping, FieldConfig, PanelEditorProps, FieldDisplayOptions } from '@grafana/data';
|
import { PanelEditorProps, FieldDisplayOptions, ValueMapping, FieldConfig } from '@grafana/data';
|
||||||
|
|
||||||
import { PieChartOptionsBox } from './PieChartOptionsBox';
|
import { PieChartOptionsBox } from './PieChartOptionsBox';
|
||||||
import { PieChartOptions } from './types';
|
import { PieChartOptions } from './types';
|
||||||
|
|
||||||
export class PieChartPanelEditor extends PureComponent<PanelEditorProps<PieChartOptions>> {
|
export class PieChartPanelEditor extends PureComponent<PanelEditorProps<PieChartOptions>> {
|
||||||
onValueMappingsChanged = (mappings: ValueMapping[]) => {
|
onValueMappingsChanged = (mappings: ValueMapping[]) => {
|
||||||
const current = this.props.options.fieldOptions.defaults;
|
const current = this.props.fieldConfig;
|
||||||
this.onDefaultsChange({
|
this.props.onFieldConfigChange({
|
||||||
...current,
|
...current,
|
||||||
mappings,
|
defaults: {
|
||||||
|
...current.defaults,
|
||||||
|
mappings,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -27,16 +30,16 @@ export class PieChartPanelEditor extends PureComponent<PanelEditorProps<PieChart
|
|||||||
});
|
});
|
||||||
|
|
||||||
onDefaultsChange = (field: FieldConfig) => {
|
onDefaultsChange = (field: FieldConfig) => {
|
||||||
this.onDisplayOptionsChanged({
|
this.props.onFieldConfigChange({
|
||||||
...this.props.options.fieldOptions,
|
...this.props.fieldConfig,
|
||||||
defaults: field,
|
defaults: field,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { onOptionsChange, options, data } = this.props;
|
const { onOptionsChange, options, data, fieldConfig, onFieldConfigChange } = this.props;
|
||||||
const { fieldOptions } = options;
|
const { fieldOptions } = options;
|
||||||
const { defaults } = fieldOptions;
|
const { defaults } = fieldConfig;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
@@ -49,10 +52,15 @@ export class PieChartPanelEditor extends PureComponent<PanelEditorProps<PieChart
|
|||||||
<FieldPropertiesEditor showMinMax={true} onChange={this.onDefaultsChange} value={defaults} />
|
<FieldPropertiesEditor showMinMax={true} onChange={this.onDefaultsChange} value={defaults} />
|
||||||
</PanelOptionsGroup>
|
</PanelOptionsGroup>
|
||||||
|
|
||||||
<PieChartOptionsBox data={data} onOptionsChange={onOptionsChange} options={options} />
|
<PieChartOptionsBox
|
||||||
|
data={data}
|
||||||
|
onOptionsChange={onOptionsChange}
|
||||||
|
options={options}
|
||||||
|
fieldConfig={fieldConfig}
|
||||||
|
onFieldConfigChange={onFieldConfigChange}
|
||||||
|
/>
|
||||||
</PanelOptionsGrid>
|
</PanelOptionsGrid>
|
||||||
|
<LegacyValueMappingsEditor onChange={this.onValueMappingsChanged} valueMappings={defaults.mappings} />
|
||||||
<ValueMappingsEditor onChange={this.onValueMappingsChanged} valueMappings={defaults.mappings} />
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,4 +5,5 @@ import { PieChartOptions, defaults } from './types';
|
|||||||
|
|
||||||
export const plugin = new PanelPlugin<PieChartOptions>(PieChartPanel)
|
export const plugin = new PanelPlugin<PieChartOptions>(PieChartPanel)
|
||||||
.setDefaults(defaults)
|
.setDefaults(defaults)
|
||||||
|
.setFieldConfigDefaults({ defaults: { unit: 'short' } })
|
||||||
.setEditor(PieChartPanelEditor);
|
.setEditor(PieChartPanelEditor);
|
||||||
|
|||||||
@@ -14,8 +14,5 @@ export const defaults: PieChartOptions = {
|
|||||||
fieldOptions: {
|
fieldOptions: {
|
||||||
...standardFieldDisplayOptions,
|
...standardFieldDisplayOptions,
|
||||||
calcs: [ReducerID.last],
|
calcs: [ReducerID.last],
|
||||||
defaults: {
|
|
||||||
unit: 'short',
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -69,10 +69,11 @@ export class StatPanel extends PureComponent<PanelProps<StatPanelOptions>> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
getValues = (): FieldDisplay[] => {
|
getValues = (): FieldDisplay[] => {
|
||||||
const { data, options, replaceVariables } = this.props;
|
const { data, options, replaceVariables, fieldConfig } = this.props;
|
||||||
|
|
||||||
return getFieldDisplayValues({
|
return getFieldDisplayValues({
|
||||||
...options,
|
fieldConfig,
|
||||||
|
fieldOptions: options.fieldOptions,
|
||||||
replaceVariables,
|
replaceVariables,
|
||||||
theme: config.theme,
|
theme: config.theme,
|
||||||
data: data.series,
|
data: data.series,
|
||||||
|
|||||||
@@ -2,48 +2,53 @@
|
|||||||
import React, { PureComponent } from 'react';
|
import React, { PureComponent } from 'react';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
ThresholdsEditor,
|
|
||||||
PanelOptionsGrid,
|
PanelOptionsGrid,
|
||||||
ValueMappingsEditor,
|
|
||||||
FieldDisplayEditor,
|
FieldDisplayEditor,
|
||||||
FieldPropertiesEditor,
|
|
||||||
PanelOptionsGroup,
|
PanelOptionsGroup,
|
||||||
DataLinksEditor,
|
|
||||||
FormLabel,
|
FormLabel,
|
||||||
Select,
|
Select,
|
||||||
|
FieldPropertiesEditor,
|
||||||
|
ThresholdsEditor,
|
||||||
|
LegacyValueMappingsEditor,
|
||||||
|
DataLinksEditor,
|
||||||
} from '@grafana/ui';
|
} from '@grafana/ui';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
ThresholdsConfig,
|
|
||||||
ValueMapping,
|
|
||||||
FieldConfig,
|
|
||||||
DataLink,
|
|
||||||
PanelEditorProps,
|
PanelEditorProps,
|
||||||
FieldDisplayOptions,
|
FieldDisplayOptions,
|
||||||
|
FieldConfig,
|
||||||
|
ValueMapping,
|
||||||
|
ThresholdsConfig,
|
||||||
|
DataLink,
|
||||||
} from '@grafana/data';
|
} from '@grafana/data';
|
||||||
|
|
||||||
import { StatPanelOptions, colorModes, graphModes, justifyModes } from './types';
|
import { StatPanelOptions, colorModes, graphModes, justifyModes } from './types';
|
||||||
import { orientationOptions } from '../gauge/types';
|
import { orientationOptions } from '../gauge/types';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
getDataLinksVariableSuggestions,
|
|
||||||
getCalculationValueDataLinksVariableSuggestions,
|
getCalculationValueDataLinksVariableSuggestions,
|
||||||
} from 'app/features/panel/panellinks/link_srv';
|
getDataLinksVariableSuggestions,
|
||||||
|
} from '../../../features/panel/panellinks/link_srv';
|
||||||
|
|
||||||
export class StatPanelEditor extends PureComponent<PanelEditorProps<StatPanelOptions>> {
|
export class StatPanelEditor extends PureComponent<PanelEditorProps<StatPanelOptions>> {
|
||||||
onThresholdsChanged = (thresholds: ThresholdsConfig) => {
|
onThresholdsChanged = (thresholds: ThresholdsConfig) => {
|
||||||
const current = this.props.options.fieldOptions.defaults;
|
const current = this.props.fieldConfig;
|
||||||
this.onDefaultsChange({
|
this.props.onFieldConfigChange({
|
||||||
...current,
|
...current,
|
||||||
thresholds,
|
defaults: {
|
||||||
|
...current.defaults,
|
||||||
|
thresholds,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
onValueMappingsChanged = (mappings: ValueMapping[]) => {
|
onValueMappingsChanged = (mappings: ValueMapping[]) => {
|
||||||
const current = this.props.options.fieldOptions.defaults;
|
const current = this.props.fieldConfig;
|
||||||
this.onDefaultsChange({
|
this.props.onFieldConfigChange({
|
||||||
...current,
|
...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 });
|
onOrientationChange = ({ value }: any) => this.props.onOptionsChange({ ...this.props.options, orientation: value });
|
||||||
|
|
||||||
onDefaultsChange = (field: FieldConfig) => {
|
onDefaultsChange = (field: FieldConfig) => {
|
||||||
this.onDisplayOptionsChanged({
|
this.props.onFieldConfigChange({
|
||||||
...this.props.options.fieldOptions,
|
...this.props.fieldConfig,
|
||||||
defaults: field,
|
defaults: field,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
onDataLinksChanged = (links: DataLink[]) => {
|
onDataLinksChanged = (links: DataLink[]) => {
|
||||||
this.onDefaultsChange({
|
const current = this.props.fieldConfig;
|
||||||
...this.props.options.fieldOptions.defaults,
|
this.props.onFieldConfigChange({
|
||||||
links,
|
...current,
|
||||||
|
defaults: {
|
||||||
|
...current.defaults,
|
||||||
|
links,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { options } = this.props;
|
const { options, fieldConfig } = this.props;
|
||||||
const { fieldOptions } = options;
|
const { fieldOptions } = options;
|
||||||
const { defaults } = fieldOptions;
|
const { defaults } = fieldConfig;
|
||||||
|
|
||||||
const suggestions = fieldOptions.values
|
const suggestions = fieldOptions.values
|
||||||
? getDataLinksVariableSuggestions(this.props.data.series)
|
? getDataLinksVariableSuggestions(this.props.data.series)
|
||||||
: getCalculationValueDataLinksVariableSuggestions(this.props.data.series);
|
: getCalculationValueDataLinksVariableSuggestions(this.props.data.series);
|
||||||
@@ -126,7 +136,6 @@ export class StatPanelEditor extends PureComponent<PanelEditorProps<StatPanelOpt
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</PanelOptionsGroup>
|
</PanelOptionsGroup>
|
||||||
|
|
||||||
<PanelOptionsGroup title="Field">
|
<PanelOptionsGroup title="Field">
|
||||||
<FieldPropertiesEditor
|
<FieldPropertiesEditor
|
||||||
showMinMax={true}
|
showMinMax={true}
|
||||||
@@ -138,8 +147,7 @@ export class StatPanelEditor extends PureComponent<PanelEditorProps<StatPanelOpt
|
|||||||
|
|
||||||
<ThresholdsEditor onChange={this.onThresholdsChanged} thresholds={defaults.thresholds} />
|
<ThresholdsEditor onChange={this.onThresholdsChanged} thresholds={defaults.thresholds} />
|
||||||
</PanelOptionsGrid>
|
</PanelOptionsGrid>
|
||||||
|
<LegacyValueMappingsEditor onChange={this.onValueMappingsChanged} valueMappings={defaults.mappings} />
|
||||||
<ValueMappingsEditor onChange={this.onValueMappingsChanged} valueMappings={defaults.mappings} />
|
|
||||||
|
|
||||||
<PanelOptionsGroup title="Data links">
|
<PanelOptionsGroup title="Data links">
|
||||||
<DataLinksEditor
|
<DataLinksEditor
|
||||||
|
|||||||
@@ -1,11 +1,12 @@
|
|||||||
import { sharedSingleStatMigrationHandler, sharedSingleStatPanelChangedHandler } from '@grafana/ui';
|
import { sharedSingleStatMigrationHandler, sharedSingleStatPanelChangedHandler } from '@grafana/ui';
|
||||||
import { PanelPlugin } from '@grafana/data';
|
import { PanelPlugin } from '@grafana/data';
|
||||||
import { StatPanelOptions, defaults } from './types';
|
import { StatPanelOptions, defaults, standardFieldConfig } from './types';
|
||||||
import { StatPanel } from './StatPanel';
|
import { StatPanel } from './StatPanel';
|
||||||
import { StatPanelEditor } from './StatPanelEditor';
|
import { StatPanelEditor } from './StatPanelEditor';
|
||||||
|
|
||||||
export const plugin = new PanelPlugin<StatPanelOptions>(StatPanel)
|
export const plugin = new PanelPlugin<StatPanelOptions>(StatPanel)
|
||||||
.setDefaults(defaults)
|
.setDefaults(defaults)
|
||||||
|
.setFieldConfigDefaults(standardFieldConfig)
|
||||||
.setEditor(StatPanelEditor)
|
.setEditor(StatPanelEditor)
|
||||||
.setNoPadding()
|
.setNoPadding()
|
||||||
.setPanelChangeHandler(sharedSingleStatPanelChangedHandler)
|
.setPanelChangeHandler(sharedSingleStatPanelChangedHandler)
|
||||||
|
|||||||
@@ -1,5 +1,12 @@
|
|||||||
import { SingleStatBaseOptions, BigValueColorMode, BigValueGraphMode, BigValueJustifyMode } from '@grafana/ui';
|
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
|
// Structure copied from angular
|
||||||
export interface StatPanelOptions extends SingleStatBaseOptions {
|
export interface StatPanelOptions extends SingleStatBaseOptions {
|
||||||
@@ -26,6 +33,9 @@ export const justifyModes: Array<SelectableValue<BigValueJustifyMode>> = [
|
|||||||
export const standardFieldDisplayOptions: FieldDisplayOptions = {
|
export const standardFieldDisplayOptions: FieldDisplayOptions = {
|
||||||
values: false,
|
values: false,
|
||||||
calcs: [ReducerID.mean],
|
calcs: [ReducerID.mean],
|
||||||
|
};
|
||||||
|
|
||||||
|
export const standardFieldConfig: FieldConfigSource = {
|
||||||
defaults: {
|
defaults: {
|
||||||
thresholds: {
|
thresholds: {
|
||||||
mode: ThresholdsMode.Absolute,
|
mode: ThresholdsMode.Absolute,
|
||||||
|
|||||||
@@ -1,14 +1,7 @@
|
|||||||
import { FieldConfigSource } from '@grafana/data';
|
|
||||||
|
|
||||||
export interface Options {
|
export interface Options {
|
||||||
fieldOptions: FieldConfigSource;
|
|
||||||
showHeader: boolean;
|
showHeader: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const defaults: Options = {
|
export const defaults: Options = {
|
||||||
fieldOptions: {
|
|
||||||
defaults: {},
|
|
||||||
overrides: [],
|
|
||||||
},
|
|
||||||
showHeader: true,
|
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