Commit 21948e80 by Ryan McKinley Committed by GitHub

SingleStat: add a gauge migration call to action button in the editor (#18604)

parent d1860df8
import { sharedSingleStatMigrationCheck } from './SingleStatBaseOptions'; import { sharedSingleStatMigrationHandler } from './SingleStatBaseOptions';
describe('sharedSingleStatMigrationCheck', () => { describe('sharedSingleStatMigrationHandler', () => {
it('from old valueOptions model without pluginVersion', () => { it('from old valueOptions model without pluginVersion', () => {
const panel = { const panel = {
options: { options: {
...@@ -34,6 +34,6 @@ describe('sharedSingleStatMigrationCheck', () => { ...@@ -34,6 +34,6 @@ describe('sharedSingleStatMigrationCheck', () => {
type: 'bargauge', type: 'bargauge',
}; };
expect(sharedSingleStatMigrationCheck(panel as any)).toMatchSnapshot(); expect(sharedSingleStatMigrationHandler(panel as any)).toMatchSnapshot();
}); });
}); });
...@@ -3,7 +3,15 @@ import omit from 'lodash/omit'; ...@@ -3,7 +3,15 @@ import omit from 'lodash/omit';
import { VizOrientation, PanelModel } from '../../types/panel'; import { VizOrientation, PanelModel } from '../../types/panel';
import { FieldDisplayOptions } from '../../utils/fieldDisplay'; import { FieldDisplayOptions } from '../../utils/fieldDisplay';
import { fieldReducers, Threshold, sortThresholds } from '@grafana/data'; import {
fieldReducers,
Threshold,
sortThresholds,
FieldConfig,
ReducerID,
ValueMapping,
MappingType,
} from '@grafana/data';
export interface SingleStatBaseOptions { export interface SingleStatBaseOptions {
fieldOptions: FieldDisplayOptions; fieldOptions: FieldDisplayOptions;
...@@ -12,23 +20,82 @@ export interface SingleStatBaseOptions { ...@@ -12,23 +20,82 @@ export interface SingleStatBaseOptions {
const optionsToKeep = ['fieldOptions', 'orientation']; const optionsToKeep = ['fieldOptions', 'orientation'];
export const sharedSingleStatOptionsCheck = ( export function sharedSingleStatPanelChangedHandler(
options: Partial<SingleStatBaseOptions> | any, options: Partial<SingleStatBaseOptions> | any,
prevPluginId: string, prevPluginId: string,
prevOptions: any prevOptions: any
) => { ) {
// Migrating from angular singlestat
if (prevPluginId === 'singlestat' && prevOptions.angular) {
const panel = prevOptions.angular;
const reducer = fieldReducers.getIfExists(panel.valueName);
const options = {
fieldOptions: {
defaults: {} as FieldConfig,
override: {} as FieldConfig,
calcs: [reducer ? reducer.id : ReducerID.mean],
},
orientation: VizOrientation.Horizontal,
};
const defaults = options.fieldOptions.defaults;
if (panel.format) {
defaults.unit = panel.format;
}
if (panel.nullPointMode) {
defaults.nullValueMode = panel.nullPointMode;
}
if (panel.nullText) {
defaults.noValue = panel.nullText;
}
if (panel.decimals || panel.decimals === 0) {
defaults.decimals = panel.decimals;
}
// Convert thresholds and color values
if (panel.thresholds && panel.colors) {
const levels = panel.thresholds.split(',').map((strVale: string) => {
return Number(strVale.trim());
});
// One more color than threshold
const thresholds: Threshold[] = [];
for (const color of panel.colors) {
const idx = thresholds.length - 1;
if (idx >= 0) {
thresholds.push({ value: levels[idx], color });
} else {
thresholds.push({ value: -Infinity, color });
}
}
defaults.thresholds = thresholds;
}
// Convert value mappings
const mappings = convertOldAngulrValueMapping(panel);
if (mappings && mappings.length) {
defaults.mappings = mappings;
}
if (panel.gauge) {
defaults.min = panel.gauge.minValue;
defaults.max = panel.gauge.maxValue;
}
return options;
}
for (const k of optionsToKeep) { for (const k of optionsToKeep) {
if (prevOptions.hasOwnProperty(k)) { if (prevOptions.hasOwnProperty(k)) {
options[k] = cloneDeep(prevOptions[k]); options[k] = cloneDeep(prevOptions[k]);
} }
} }
return options; return options;
}; }
export function sharedSingleStatMigrationCheck(panel: PanelModel<SingleStatBaseOptions>) { export function sharedSingleStatMigrationHandler(panel: PanelModel<SingleStatBaseOptions>): SingleStatBaseOptions {
if (!panel.options) { if (!panel.options) {
// This happens on the first load or when migrating from angular // This happens on the first load or when migrating from angular
return {}; return {} as any;
} }
const previousVersion = parseFloat(panel.pluginVersion || '6.1'); const previousVersion = parseFloat(panel.pluginVersion || '6.1');
...@@ -121,3 +188,43 @@ export function migrateOldThresholds(thresholds?: any[]): Threshold[] | undefine ...@@ -121,3 +188,43 @@ export function migrateOldThresholds(thresholds?: any[]): Threshold[] | undefine
copy[0].value = -Infinity; copy[0].value = -Infinity;
return copy; return copy;
} }
/**
* Convert the angular single stat mapping to new react style
*/
function convertOldAngulrValueMapping(panel: any): ValueMapping[] {
const mappings: ValueMapping[] = [];
// Guess the right type based on options
let mappingType = panel.mappingType;
if (!panel.mappingType) {
if (panel.valueMaps && panel.valueMaps.length) {
mappingType = 1;
} else if (panel.rangeMaps && panel.rangeMaps.length) {
mappingType = 2;
}
}
// check value to text mappings if its enabled
if (mappingType === 1) {
for (let i = 0; i < panel.valueMaps.length; i++) {
const map = panel.valueMaps[i];
mappings.push({
...map,
id: i, // used for order
type: MappingType.ValueToText,
});
}
} else if (mappingType === 2) {
for (let i = 0; i < panel.rangeMaps.length; i++) {
const map = panel.rangeMaps[i];
mappings.push({
...map,
id: i, // used for order
type: MappingType.RangeToText,
});
}
}
return mappings;
}
// Jest Snapshot v1, https://goo.gl/fbAQLP // Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`sharedSingleStatMigrationCheck from old valueOptions model without pluginVersion 1`] = ` exports[`sharedSingleStatMigrationHandler from old valueOptions model without pluginVersion 1`] = `
Object { Object {
"fieldOptions": Object { "fieldOptions": Object {
"calcs": Array [ "calcs": Array [
......
...@@ -3,6 +3,6 @@ export { FieldPropertiesEditor } from './FieldPropertiesEditor'; ...@@ -3,6 +3,6 @@ export { FieldPropertiesEditor } from './FieldPropertiesEditor';
export { export {
SingleStatBaseOptions, SingleStatBaseOptions,
sharedSingleStatOptionsCheck, sharedSingleStatPanelChangedHandler,
sharedSingleStatMigrationCheck, sharedSingleStatMigrationHandler,
} from './SingleStatBaseOptions'; } from './SingleStatBaseOptions';
...@@ -61,7 +61,7 @@ export class DashboardPanel extends PureComponent<Props, State> { ...@@ -61,7 +61,7 @@ export class DashboardPanel extends PureComponent<Props, State> {
return <AddPanelWidget panel={this.props.panel} dashboard={this.props.dashboard} />; return <AddPanelWidget panel={this.props.panel} dashboard={this.props.dashboard} />;
} }
onPluginTypeChanged = (plugin: PanelPluginMeta) => { onPluginTypeChange = (plugin: PanelPluginMeta) => {
this.loadPlugin(plugin.id); this.loadPlugin(plugin.id);
}; };
...@@ -211,7 +211,7 @@ export class DashboardPanel extends PureComponent<Props, State> { ...@@ -211,7 +211,7 @@ export class DashboardPanel extends PureComponent<Props, State> {
plugin={plugin} plugin={plugin}
dashboard={dashboard} dashboard={dashboard}
angularPanel={angularPanel} angularPanel={angularPanel}
onTypeChanged={this.onPluginTypeChanged} onPluginTypeChange={this.onPluginTypeChange}
/> />
)} )}
</div> </div>
......
...@@ -20,7 +20,7 @@ interface PanelEditorProps { ...@@ -20,7 +20,7 @@ interface PanelEditorProps {
dashboard: DashboardModel; dashboard: DashboardModel;
plugin: PanelPlugin; plugin: PanelPlugin;
angularPanel?: AngularComponent; angularPanel?: AngularComponent;
onTypeChanged: (newType: PanelPluginMeta) => void; onPluginTypeChange: (newType: PanelPluginMeta) => void;
} }
interface PanelEditorTab { interface PanelEditorTab {
...@@ -70,7 +70,7 @@ export class PanelEditor extends PureComponent<PanelEditorProps> { ...@@ -70,7 +70,7 @@ export class PanelEditor extends PureComponent<PanelEditorProps> {
}; };
renderCurrentTab(activeTab: string) { renderCurrentTab(activeTab: string) {
const { panel, dashboard, onTypeChanged, plugin, angularPanel } = this.props; const { panel, dashboard, onPluginTypeChange, plugin, angularPanel } = this.props;
switch (activeTab) { switch (activeTab) {
case 'advanced': case 'advanced':
...@@ -85,7 +85,7 @@ export class PanelEditor extends PureComponent<PanelEditorProps> { ...@@ -85,7 +85,7 @@ export class PanelEditor extends PureComponent<PanelEditorProps> {
panel={panel} panel={panel}
dashboard={dashboard} dashboard={dashboard}
plugin={plugin} plugin={plugin}
onTypeChanged={onTypeChanged} onPluginTypeChange={onPluginTypeChange}
angularPanel={angularPanel} angularPanel={angularPanel}
/> />
); );
......
...@@ -19,13 +19,14 @@ import { DashboardModel } from '../state'; ...@@ -19,13 +19,14 @@ import { 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 { PanelPlugin, PanelPluginMeta } from '@grafana/ui'; import { PanelPlugin, PanelPluginMeta } from '@grafana/ui';
import { PanelCtrl } from 'app/plugins/sdk';
interface Props { interface Props {
panel: PanelModel; panel: PanelModel;
dashboard: DashboardModel; dashboard: DashboardModel;
plugin: PanelPlugin; plugin: PanelPlugin;
angularPanel?: AngularComponent; angularPanel?: AngularComponent;
onTypeChanged: (newType: PanelPluginMeta) => void; onPluginTypeChange: (newType: PanelPluginMeta) => void;
updateLocation: typeof updateLocation; updateLocation: typeof updateLocation;
urlOpenVizPicker: boolean; urlOpenVizPicker: boolean;
} }
...@@ -104,8 +105,9 @@ export class VisualizationTab extends PureComponent<Props, State> { ...@@ -104,8 +105,9 @@ export class VisualizationTab extends PureComponent<Props, State> {
return; return;
} }
const panelCtrl = scope.$$childHead.ctrl; const panelCtrl: PanelCtrl = scope.$$childHead.ctrl;
panelCtrl.initEditMode(); panelCtrl.initEditMode();
panelCtrl.onPluginTypeChange = this.onPluginTypeChange;
let template = ''; let template = '';
for (let i = 0; i < panelCtrl.editorTabs.length; i++) { for (let i = 0; i < panelCtrl.editorTabs.length; i++) {
...@@ -197,11 +199,11 @@ export class VisualizationTab extends PureComponent<Props, State> { ...@@ -197,11 +199,11 @@ export class VisualizationTab extends PureComponent<Props, State> {
} }
}; };
onTypeChanged = (plugin: PanelPluginMeta) => { onPluginTypeChange = (plugin: PanelPluginMeta) => {
if (plugin.id === this.props.plugin.meta.id) { if (plugin.id === this.props.plugin.meta.id) {
this.setState({ isVizPickerOpen: false }); this.setState({ isVizPickerOpen: false });
} else { } else {
this.props.onTypeChanged(plugin); this.props.onPluginTypeChange(plugin);
} }
}; };
...@@ -235,7 +237,7 @@ export class VisualizationTab extends PureComponent<Props, State> { ...@@ -235,7 +237,7 @@ export class VisualizationTab extends PureComponent<Props, State> {
<FadeIn in={isVizPickerOpen} duration={200} unmountOnExit={true} onExited={this.clearQuery}> <FadeIn in={isVizPickerOpen} duration={200} unmountOnExit={true} onExited={this.clearQuery}>
<VizTypePicker <VizTypePicker
current={meta} current={meta}
onTypeChanged={this.onTypeChanged} onTypeChange={this.onPluginTypeChange}
searchQuery={searchQuery} searchQuery={searchQuery}
onClose={this.onCloseVizPicker} onClose={this.onCloseVizPicker}
/> />
......
...@@ -6,7 +6,7 @@ import { PanelPluginMeta, EmptySearchResult } from '@grafana/ui'; ...@@ -6,7 +6,7 @@ import { PanelPluginMeta, EmptySearchResult } from '@grafana/ui';
export interface Props { export interface Props {
current: PanelPluginMeta; current: PanelPluginMeta;
onTypeChanged: (newType: PanelPluginMeta) => void; onTypeChange: (newType: PanelPluginMeta) => void;
searchQuery: string; searchQuery: string;
onClose: () => void; onClose: () => void;
} }
...@@ -34,16 +34,11 @@ export class VizTypePicker extends PureComponent<Props> { ...@@ -34,16 +34,11 @@ export class VizTypePicker extends PureComponent<Props> {
} }
renderVizPlugin = (plugin: PanelPluginMeta, index: number) => { renderVizPlugin = (plugin: PanelPluginMeta, index: number) => {
const { onTypeChanged } = this.props; const { onTypeChange } = this.props;
const isCurrent = plugin.id === this.props.current.id; const isCurrent = plugin.id === this.props.current.id;
return ( return (
<VizTypePickerPlugin <VizTypePickerPlugin key={plugin.id} isCurrent={isCurrent} plugin={plugin} onClick={() => onTypeChange(plugin)} />
key={plugin.id}
isCurrent={isCurrent}
plugin={plugin}
onClick={() => onTypeChanged(plugin)}
/>
); );
}; };
......
...@@ -165,7 +165,7 @@ describe('PanelModel', () => { ...@@ -165,7 +165,7 @@ describe('PanelModel', () => {
it('should call react onPanelTypeChanged', () => { it('should call react onPanelTypeChanged', () => {
expect(onPanelTypeChanged.mock.calls.length).toBe(1); expect(onPanelTypeChanged.mock.calls.length).toBe(1);
expect(onPanelTypeChanged.mock.calls[0][1]).toBe('table'); expect(onPanelTypeChanged.mock.calls[0][1]).toBe('table');
expect(onPanelTypeChanged.mock.calls[0][2].fieldOptions).toBeDefined(); expect(onPanelTypeChanged.mock.calls[0][2].angular).toBeDefined();
}); });
it('getQueryRunner() should return same instance after changing to another react panel', () => { it('getQueryRunner() should return same instance after changing to another react panel', () => {
......
...@@ -262,9 +262,10 @@ export class PanelModel { ...@@ -262,9 +262,10 @@ export class PanelModel {
const pluginId = newPlugin.meta.id; const pluginId = newPlugin.meta.id;
const oldOptions: any = this.getOptionsToRemember(); const oldOptions: any = this.getOptionsToRemember();
const oldPluginId = this.type; const oldPluginId = this.type;
const wasAngular = !!this.plugin.angularPanelCtrl;
// for angular panels we must remove all events and let angular panels do some cleanup // for angular panels we must remove all events and let angular panels do some cleanup
if (this.plugin.angularPanelCtrl) { if (wasAngular) {
this.destroy(); this.destroy();
} }
...@@ -280,17 +281,25 @@ export class PanelModel { ...@@ -280,17 +281,25 @@ export class PanelModel {
this.cachedPluginOptions[oldPluginId] = oldOptions; this.cachedPluginOptions[oldPluginId] = oldOptions;
this.restorePanelOptions(pluginId); this.restorePanelOptions(pluginId);
// switch
this.type = pluginId;
this.plugin = newPlugin;
this.applyPluginOptionDefaults(newPlugin);
// Let panel plugins inspect options from previous panel and keep any that it can use // Let panel plugins inspect options from previous panel and keep any that it can use
if (newPlugin.onPanelTypeChanged) { if (newPlugin.onPanelTypeChanged) {
let old: any = {};
if (wasAngular) {
old = { angular: oldOptions };
} else if (oldOptions && oldOptions.options) {
old = oldOptions.options;
}
this.options = this.options || {}; this.options = this.options || {};
const old = oldOptions && oldOptions.options ? oldOptions.options : {};
Object.assign(this.options, newPlugin.onPanelTypeChanged(this.options, oldPluginId, old)); Object.assign(this.options, newPlugin.onPanelTypeChanged(this.options, oldPluginId, old));
} }
// switch
this.type = pluginId;
this.plugin = newPlugin;
this.applyPluginOptionDefaults(newPlugin);
if (newPlugin.onPanelMigration) { if (newPlugin.onPanelMigration) {
this.pluginVersion = getPluginVersion(newPlugin); this.pluginVersion = getPluginVersion(newPlugin);
} }
......
...@@ -19,6 +19,7 @@ import { GRID_COLUMN_COUNT } from 'app/core/constants'; ...@@ -19,6 +19,7 @@ import { GRID_COLUMN_COUNT } from 'app/core/constants';
import { auto } from 'angular'; import { auto } from 'angular';
import { TemplateSrv } from '../templating/template_srv'; import { TemplateSrv } from '../templating/template_srv';
import { LinkSrv } from './panellinks/link_srv'; import { LinkSrv } from './panellinks/link_srv';
import { PanelPluginMeta } from '@grafana/ui/src/types/panel';
export class PanelCtrl { export class PanelCtrl {
panel: any; panel: any;
...@@ -281,4 +282,7 @@ export class PanelCtrl { ...@@ -281,4 +282,7 @@ export class PanelCtrl {
html += '</div>'; html += '</div>';
return html; return html;
} }
// overriden from react
onPluginTypeChange = (plugin: PanelPluginMeta) => {};
} }
import { PanelModel } from '@grafana/ui'; import { PanelModel } from '@grafana/ui';
import { barGaugePanelMigrationCheck } from './BarGaugeMigrations'; import { barGaugePanelMigrationHandler } from './BarGaugeMigrations';
describe('BarGauge Panel Migrations', () => { describe('BarGauge Panel Migrations', () => {
it('from 6.2', () => { it('from 6.2', () => {
...@@ -45,6 +45,6 @@ describe('BarGauge Panel Migrations', () => { ...@@ -45,6 +45,6 @@ describe('BarGauge Panel Migrations', () => {
type: 'bargauge', type: 'bargauge',
} as PanelModel; } as PanelModel;
expect(barGaugePanelMigrationCheck(panel)).toMatchSnapshot(); expect(barGaugePanelMigrationHandler(panel)).toMatchSnapshot();
}); });
}); });
import { PanelModel } from '@grafana/ui'; import { PanelModel, sharedSingleStatMigrationHandler } from '@grafana/ui';
import { sharedSingleStatMigrationCheck } from '@grafana/ui/src/components/SingleStatShared/SingleStatBaseOptions';
import { BarGaugeOptions } from './types'; import { BarGaugeOptions } from './types';
export const barGaugePanelMigrationCheck = (panel: PanelModel<BarGaugeOptions>): Partial<BarGaugeOptions> => { export const barGaugePanelMigrationHandler = (panel: PanelModel<BarGaugeOptions>): Partial<BarGaugeOptions> => {
return sharedSingleStatMigrationCheck(panel); return sharedSingleStatMigrationHandler(panel);
}; };
import { PanelPlugin, sharedSingleStatOptionsCheck } from '@grafana/ui'; import { PanelPlugin, sharedSingleStatPanelChangedHandler } from '@grafana/ui';
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 { barGaugePanelMigrationCheck } from './BarGaugeMigrations'; import { barGaugePanelMigrationHandler } from './BarGaugeMigrations';
export const plugin = new PanelPlugin<BarGaugeOptions>(BarGaugePanel) export const plugin = new PanelPlugin<BarGaugeOptions>(BarGaugePanel)
.setDefaults(defaults) .setDefaults(defaults)
.setEditor(BarGaugePanelEditor) .setEditor(BarGaugePanelEditor)
.setPanelChangeHandler(sharedSingleStatOptionsCheck) .setPanelChangeHandler(sharedSingleStatPanelChangedHandler)
.setMigrationHandler(barGaugePanelMigrationCheck); .setMigrationHandler(barGaugePanelMigrationHandler);
import { PanelModel } from '@grafana/ui'; import { PanelModel } from '@grafana/ui';
import { gaugePanelMigrationCheck } from './GaugeMigrations'; import { gaugePanelMigrationHandler, gaugePanelChangedHandler } from './GaugeMigrations';
describe('Gauge Panel Migrations', () => { describe('Gauge Panel Migrations', () => {
it('from 6.1.1', () => { it('from 6.1.1', () => {
...@@ -77,6 +77,30 @@ describe('Gauge Panel Migrations', () => { ...@@ -77,6 +77,30 @@ describe('Gauge Panel Migrations', () => {
type: 'gauge', type: 'gauge',
} as PanelModel; } as PanelModel;
expect(gaugePanelMigrationCheck(panel)).toMatchSnapshot(); expect(gaugePanelMigrationHandler(panel)).toMatchSnapshot();
});
it('change from angular singlestat to gauge', () => {
const old: any = {
angular: {
format: 'ms',
decimals: 7,
gauge: {
maxValue: 150,
minValue: -10,
show: true,
thresholdLabels: true,
thresholdMarkers: true,
},
},
};
const newOptions = gaugePanelChangedHandler({} as any, 'singlestat', old);
expect(newOptions.fieldOptions.defaults.unit).toBe('ms');
expect(newOptions.fieldOptions.defaults.min).toBe(-10);
expect(newOptions.fieldOptions.defaults.max).toBe(150);
expect(newOptions.fieldOptions.defaults.decimals).toBe(7);
expect(newOptions.showThresholdMarkers).toBe(true);
expect(newOptions.showThresholdLabels).toBe(true);
}); });
}); });
import { PanelModel } from '@grafana/ui'; import { PanelModel, sharedSingleStatPanelChangedHandler, sharedSingleStatMigrationHandler } from '@grafana/ui';
import { GaugeOptions } from './types'; import { GaugeOptions } from './types';
import { sharedSingleStatMigrationCheck } from '@grafana/ui/src/components/SingleStatShared/SingleStatBaseOptions';
export const gaugePanelMigrationCheck = (panel: PanelModel<GaugeOptions>): Partial<GaugeOptions> => { // This is called when the panel first loads
return sharedSingleStatMigrationCheck(panel); export const gaugePanelMigrationHandler = (panel: PanelModel<GaugeOptions>): Partial<GaugeOptions> => {
return sharedSingleStatMigrationHandler(panel);
};
// This is called when the panel changes from another panel
export const gaugePanelChangedHandler = (
options: Partial<GaugeOptions> | any,
prevPluginId: string,
prevOptions: any
) => {
// This handles most config changes
const opts = sharedSingleStatPanelChangedHandler(options, prevPluginId, prevOptions) as GaugeOptions;
// Changing from angular singlestat
if (prevPluginId === 'singlestat' && prevOptions.angular) {
const gauge = prevOptions.angular.gauge;
if (gauge) {
opts.showThresholdMarkers = gauge.thresholdMarkers;
opts.showThresholdLabels = gauge.thresholdLabels;
}
}
return opts;
}; };
import { PanelPlugin, sharedSingleStatMigrationCheck, sharedSingleStatOptionsCheck } from '@grafana/ui'; import { PanelPlugin } from '@grafana/ui';
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 { gaugePanelMigrationHandler, gaugePanelChangedHandler } from './GaugeMigrations';
export const plugin = new PanelPlugin<GaugeOptions>(GaugePanel) export const plugin = new PanelPlugin<GaugeOptions>(GaugePanel)
.setDefaults(defaults) .setDefaults(defaults)
.setEditor(GaugePanelEditor) .setEditor(GaugePanelEditor)
.setPanelChangeHandler(sharedSingleStatOptionsCheck) .setPanelChangeHandler(gaugePanelChangedHandler)
.setMigrationHandler(sharedSingleStatMigrationCheck); .setMigrationHandler(gaugePanelMigrationHandler);
<div class="editor-row"> <div class="editor-row">
<div class="grafana-info-box" ng-if="ctrl.panel.gauge.show">
<h5>Gauge Migration</h5>
<p>
Gauge visualizations within the Singlestat panel are deprecated. Please
migrate this panel to use the Gauge panel
<div class="gf-form-button-row">
<button class="btn btn-primary" ng-click="ctrl.migrateToGaugePanel(true)">
Migrate to Gauge Panel
</button>
<button class="btn btn-inverse" ng-click="ctrl.migrateToGaugePanel(false)">
Show as single stat
</button>
</div>
<br/>
<div ng-if="ctrl.panel.sparkline.show">
<b>NOTE:</b> Sparklines are not supported in the gauge panel
</div>
<div ng-if="ctrl.panel.prefix">
<b>NOTE:</b> Prefix will not be show in the gauge panel
</div>
<div ng-if="ctrl.panel.postfix">
<b>NOTE:</b> Postfix will not be show in the gauge panel
</div>
<div ng-if="ctrl.panel.links && ctrl.panel.links.length">
<b>NOTE:</b> Links will be in the upper left corner, rather than anywhere on the gauge
</div>
</p>
</div>
<div class="section gf-form-group"> <div class="section gf-form-group">
<h5 class="section-heading">Value</h5> <h5 class="section-heading">Value</h5>
......
...@@ -113,6 +113,15 @@ class SingleStatCtrl extends MetricsPanelCtrl { ...@@ -113,6 +113,15 @@ class SingleStatCtrl extends MetricsPanelCtrl {
this.unitFormats = kbn.getUnitFormats(); this.unitFormats = kbn.getUnitFormats();
} }
migrateToGaugePanel(migrate: boolean) {
if (migrate) {
this.onPluginTypeChange(config.panels['gauge']);
} else {
this.panel.gauge.show = false;
this.render();
}
}
setUnitFormat(subItem: { value: any }) { setUnitFormat(subItem: { value: any }) {
this.panel.format = subItem.value; this.panel.format = subItem.value;
this.refresh(); this.refresh();
......
import { PanelPlugin, sharedSingleStatMigrationCheck, sharedSingleStatOptionsCheck } from '@grafana/ui'; import { PanelPlugin, sharedSingleStatMigrationHandler, sharedSingleStatPanelChangedHandler } from '@grafana/ui';
import { SingleStatOptions, defaults } from './types'; import { SingleStatOptions, defaults } from './types';
import { SingleStatPanel } from './SingleStatPanel'; import { SingleStatPanel } from './SingleStatPanel';
import { SingleStatEditor } from './SingleStatEditor'; import { SingleStatEditor } from './SingleStatEditor';
...@@ -6,5 +6,5 @@ import { SingleStatEditor } from './SingleStatEditor'; ...@@ -6,5 +6,5 @@ import { SingleStatEditor } from './SingleStatEditor';
export const plugin = new PanelPlugin<SingleStatOptions>(SingleStatPanel) export const plugin = new PanelPlugin<SingleStatOptions>(SingleStatPanel)
.setDefaults(defaults) .setDefaults(defaults)
.setEditor(SingleStatEditor) .setEditor(SingleStatEditor)
.setPanelChangeHandler(sharedSingleStatOptionsCheck) .setPanelChangeHandler(sharedSingleStatPanelChangedHandler)
.setMigrationHandler(sharedSingleStatMigrationCheck); .setMigrationHandler(sharedSingleStatMigrationHandler);
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment