mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
NewPanelEditor/Panels: refactor setDefault to setPanelOptions (#23404)
* Remove deprecated setDefault usages * Add simple support for conditinal field config properties * Use new API in NewsPanel * Update tests * Fix check
This commit is contained in:
parent
a29056966c
commit
ea792edd3a
@ -60,6 +60,8 @@ export interface StringFieldConfigSettings {
|
|||||||
placeholder?: string;
|
placeholder?: string;
|
||||||
maxLength?: number;
|
maxLength?: number;
|
||||||
expandTemplateVars?: boolean;
|
expandTemplateVars?: boolean;
|
||||||
|
useTextarea?: boolean;
|
||||||
|
rows?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const stringOverrideProcessor = (
|
export const stringOverrideProcessor = (
|
||||||
|
@ -10,6 +10,7 @@ export interface OptionsEditorItem<TOptions, TSettings, TEditorProps, TValue> ex
|
|||||||
editor: ComponentType<TEditorProps>;
|
editor: ComponentType<TEditorProps>;
|
||||||
settings?: TSettings;
|
settings?: TSettings;
|
||||||
defaultValue?: TValue;
|
defaultValue?: TValue;
|
||||||
|
showIf?: (currentConfig: TOptions) => boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -60,6 +60,7 @@ export interface FieldConfigEditorConfig<TOptions, TSettings = any, TValue = any
|
|||||||
settings?: TSettings;
|
settings?: TSettings;
|
||||||
shouldApply?: (field: Field) => boolean;
|
shouldApply?: (field: Field) => boolean;
|
||||||
defaultValue?: TValue;
|
defaultValue?: TValue;
|
||||||
|
showIf?: (currentConfig: TOptions) => boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface FieldConfigPropertyItem<TOptions = any, TValue = any, TSettings extends {} = any>
|
export interface FieldConfigPropertyItem<TOptions = any, TValue = any, TSettings extends {} = any>
|
||||||
|
@ -124,6 +124,7 @@ export interface PanelOptionsEditorConfig<TOptions, TSettings = any, TValue = an
|
|||||||
description: string;
|
description: string;
|
||||||
settings?: TSettings;
|
settings?: TSettings;
|
||||||
defaultValue?: TValue;
|
defaultValue?: TValue;
|
||||||
|
showIf?: (currentConfig: TOptions) => boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface PanelMenuItem {
|
export interface PanelMenuItem {
|
||||||
|
@ -1,17 +1,20 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { FieldConfigEditorProps, StringFieldConfigSettings } from '@grafana/data';
|
import { FieldConfigEditorProps, StringFieldConfigSettings } from '@grafana/data';
|
||||||
import { Input } from '../Input/Input';
|
import { Input } from '../Input/Input';
|
||||||
|
import { TextArea } from '../Forms/TextArea/TextArea';
|
||||||
|
|
||||||
export const StringValueEditor: React.FC<FieldConfigEditorProps<string, StringFieldConfigSettings>> = ({
|
export const StringValueEditor: React.FC<FieldConfigEditorProps<string, StringFieldConfigSettings>> = ({
|
||||||
value,
|
value,
|
||||||
onChange,
|
onChange,
|
||||||
item,
|
item,
|
||||||
}) => {
|
}) => {
|
||||||
|
const Component = item.settings?.useTextarea ? TextArea : Input;
|
||||||
return (
|
return (
|
||||||
<Input
|
<Component
|
||||||
placeholder={item.settings?.placeholder}
|
placeholder={item.settings?.placeholder}
|
||||||
value={value || ''}
|
value={value || ''}
|
||||||
onChange={e => onChange(e.currentTarget.value)}
|
rows={item.settings?.useTextarea && item.settings.rows}
|
||||||
|
onChange={(e: React.FormEvent<any>) => onChange(e.currentTarget.value)}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -143,6 +143,9 @@ export const DefaultFieldConfigEditor: React.FC<Props> = ({ data, onChange, conf
|
|||||||
|
|
||||||
const renderEditor = useCallback(
|
const renderEditor = useCallback(
|
||||||
(item: FieldConfigPropertyItem) => {
|
(item: FieldConfigPropertyItem) => {
|
||||||
|
if (item.isCustom && item.showIf && !item.showIf(config.defaults.custom)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
const defaults = config.defaults;
|
const defaults = config.defaults;
|
||||||
const value = item.isCustom
|
const value = item.isCustom
|
||||||
? defaults.custom
|
? defaults.custom
|
||||||
|
@ -20,6 +20,10 @@ export const PanelOptionsEditor: React.FC<PanelOptionsEditorProps<any>> = ({ plu
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{optionEditors.list().map(e => {
|
{optionEditors.list().map(e => {
|
||||||
|
if (e.showIf && !e.showIf(options)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Forms.Field label={e.name} description={e.description} key={e.id}>
|
<Forms.Field label={e.name} description={e.description} key={e.id}>
|
||||||
<e.editor value={lodashGet(options, e.path)} onChange={value => onOptionChange(e.path, value)} item={e} />
|
<e.editor value={lodashGet(options, e.path)} onChange={value => onOptionChange(e.path, value)} item={e} />
|
||||||
|
@ -4,6 +4,7 @@ import {
|
|||||||
FieldConfigProperty,
|
FieldConfigProperty,
|
||||||
identityOverrideProcessor,
|
identityOverrideProcessor,
|
||||||
PanelProps,
|
PanelProps,
|
||||||
|
standardEditorsRegistry,
|
||||||
standardFieldConfigEditorRegistry,
|
standardFieldConfigEditorRegistry,
|
||||||
} from '@grafana/data';
|
} from '@grafana/data';
|
||||||
import { ComponentClass } from 'react';
|
import { ComponentClass } from 'react';
|
||||||
@ -37,33 +38,30 @@ export const mockStandardProperties = () => {
|
|||||||
shouldApply: () => true,
|
shouldApply: () => true,
|
||||||
};
|
};
|
||||||
|
|
||||||
return [unit, decimals];
|
const boolean = {
|
||||||
|
id: 'boolean',
|
||||||
|
path: 'boolean',
|
||||||
|
name: 'Boolean',
|
||||||
|
description: '',
|
||||||
|
// @ts-ignore
|
||||||
|
editor: () => null,
|
||||||
|
// @ts-ignore
|
||||||
|
override: () => null,
|
||||||
|
process: identityOverrideProcessor,
|
||||||
|
shouldApply: () => true,
|
||||||
|
};
|
||||||
|
|
||||||
|
return [unit, decimals, boolean];
|
||||||
};
|
};
|
||||||
|
|
||||||
standardFieldConfigEditorRegistry.setInit(() => mockStandardProperties());
|
standardFieldConfigEditorRegistry.setInit(() => mockStandardProperties());
|
||||||
|
standardEditorsRegistry.setInit(() => mockStandardProperties());
|
||||||
|
|
||||||
describe('PanelModel', () => {
|
describe('PanelModel', () => {
|
||||||
describe('when creating new panel model', () => {
|
describe('when creating new panel model', () => {
|
||||||
let model: any;
|
let model: any;
|
||||||
let modelJson: any;
|
let modelJson: any;
|
||||||
let persistedOptionsMock;
|
let persistedOptionsMock;
|
||||||
const defaultOptionsMock = {
|
|
||||||
fieldOptions: {
|
|
||||||
thresholds: [
|
|
||||||
{
|
|
||||||
color: '#F2495C',
|
|
||||||
index: 1,
|
|
||||||
value: 50,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
color: '#73BF69',
|
|
||||||
index: 0,
|
|
||||||
value: null,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
arrayWith2Values: [{ value: 'name' }, { value: 'name2' }],
|
|
||||||
showThresholds: true,
|
|
||||||
};
|
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
persistedOptionsMock = {
|
persistedOptionsMock = {
|
||||||
@ -114,11 +112,16 @@ describe('PanelModel', () => {
|
|||||||
(null as unknown) as ComponentClass<PanelProps>, // react
|
(null as unknown) as ComponentClass<PanelProps>, // react
|
||||||
TablePanelCtrl // angular
|
TablePanelCtrl // angular
|
||||||
);
|
);
|
||||||
panelPlugin.setDefaults(defaultOptionsMock);
|
|
||||||
/* panelPlugin.useStandardFieldConfig([FieldConfigOptionId.Unit, FieldConfigOptionId.Decimals], {
|
panelPlugin.setPanelOptions(builder => {
|
||||||
[FieldConfigOptionId.Unit]: 'flop',
|
builder.addBooleanSwitch({
|
||||||
[FieldConfigOptionId.Decimals]: 2,
|
name: 'Show thresholds',
|
||||||
}); */
|
path: 'showThresholds',
|
||||||
|
defaultValue: true,
|
||||||
|
description: '',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
panelPlugin.useFieldConfig({
|
panelPlugin.useFieldConfig({
|
||||||
standardOptions: [FieldConfigProperty.Unit, FieldConfigProperty.Decimals],
|
standardOptions: [FieldConfigProperty.Unit, FieldConfigProperty.Decimals],
|
||||||
standardOptionsDefaults: {
|
standardOptionsDefaults: {
|
||||||
@ -200,13 +203,17 @@ describe('PanelModel', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('when changing panel type', () => {
|
describe('when changing panel type', () => {
|
||||||
const newPanelPluginDefaults = {
|
|
||||||
showThresholdLabels: false,
|
|
||||||
};
|
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
const newPlugin = getPanelPlugin({ id: 'graph' });
|
const newPlugin = getPanelPlugin({ id: 'graph' });
|
||||||
newPlugin.setDefaults(newPanelPluginDefaults);
|
newPlugin.setPanelOptions(builder => {
|
||||||
|
builder.addBooleanSwitch({
|
||||||
|
name: 'Show thresholds labels',
|
||||||
|
path: 'showThresholdLabels',
|
||||||
|
defaultValue: false,
|
||||||
|
description: '',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
model.changePlugin(newPlugin);
|
model.changePlugin(newPlugin);
|
||||||
model.alert = { id: 2 };
|
model.alert = { id: 2 };
|
||||||
});
|
});
|
||||||
|
@ -1,195 +0,0 @@
|
|||||||
// Libraries
|
|
||||||
import React, { PureComponent, ChangeEvent } from 'react';
|
|
||||||
|
|
||||||
// Components
|
|
||||||
import { PanelOptionsGroup, PanelOptionsGrid, FormField, FormLabel, LegacyForms } from '@grafana/ui';
|
|
||||||
const { Switch } = LegacyForms;
|
|
||||||
|
|
||||||
import { PanelEditorProps, toIntegerOrUndefined, toNumberString } from '@grafana/data';
|
|
||||||
|
|
||||||
// Types
|
|
||||||
import { AnnoOptions } from './types';
|
|
||||||
import { TagBadge } from 'app/core/components/TagFilter/TagBadge';
|
|
||||||
|
|
||||||
interface State {
|
|
||||||
tag: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export class AnnoListEditor extends PureComponent<PanelEditorProps<AnnoOptions>, State> {
|
|
||||||
constructor(props: PanelEditorProps<AnnoOptions>) {
|
|
||||||
super(props);
|
|
||||||
|
|
||||||
this.state = {
|
|
||||||
tag: '',
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// Display
|
|
||||||
//-----------
|
|
||||||
|
|
||||||
onToggleShowUser = () =>
|
|
||||||
this.props.onOptionsChange({ ...this.props.options, showUser: !this.props.options.showUser });
|
|
||||||
|
|
||||||
onToggleShowTime = () =>
|
|
||||||
this.props.onOptionsChange({ ...this.props.options, showTime: !this.props.options.showTime });
|
|
||||||
|
|
||||||
onToggleShowTags = () =>
|
|
||||||
this.props.onOptionsChange({ ...this.props.options, showTags: !this.props.options.showTags });
|
|
||||||
|
|
||||||
// Navigate
|
|
||||||
//-----------
|
|
||||||
|
|
||||||
onNavigateBeforeChange = (event: ChangeEvent<HTMLInputElement>) => {
|
|
||||||
this.props.onOptionsChange({ ...this.props.options, navigateBefore: event.target.value });
|
|
||||||
};
|
|
||||||
|
|
||||||
onNavigateAfterChange = (event: ChangeEvent<HTMLInputElement>) => {
|
|
||||||
this.props.onOptionsChange({ ...this.props.options, navigateAfter: event.target.value });
|
|
||||||
};
|
|
||||||
|
|
||||||
onToggleNavigateToPanel = () =>
|
|
||||||
this.props.onOptionsChange({ ...this.props.options, navigateToPanel: !this.props.options.navigateToPanel });
|
|
||||||
|
|
||||||
// Search
|
|
||||||
//-----------
|
|
||||||
onLimitChange = (event: ChangeEvent<HTMLInputElement>) => {
|
|
||||||
const v = toIntegerOrUndefined(event.target.value);
|
|
||||||
this.props.onOptionsChange({ ...this.props.options, limit: v });
|
|
||||||
};
|
|
||||||
|
|
||||||
onToggleOnlyFromThisDashboard = () =>
|
|
||||||
this.props.onOptionsChange({
|
|
||||||
...this.props.options,
|
|
||||||
onlyFromThisDashboard: !this.props.options.onlyFromThisDashboard,
|
|
||||||
});
|
|
||||||
|
|
||||||
onToggleOnlyInTimeRange = () =>
|
|
||||||
this.props.onOptionsChange({ ...this.props.options, onlyInTimeRange: !this.props.options.onlyInTimeRange });
|
|
||||||
|
|
||||||
// Tags
|
|
||||||
//-----------
|
|
||||||
|
|
||||||
onTagTextChange = (event: ChangeEvent<HTMLInputElement>) => {
|
|
||||||
this.setState({ tag: event.target.value });
|
|
||||||
};
|
|
||||||
|
|
||||||
onTagClick = (e: React.SyntheticEvent, tag: string) => {
|
|
||||||
e.stopPropagation();
|
|
||||||
|
|
||||||
const tags = this.props.options.tags.filter(item => item !== tag);
|
|
||||||
this.props.onOptionsChange({
|
|
||||||
...this.props.options,
|
|
||||||
tags,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
renderTags = (tags: string[]): JSX.Element => {
|
|
||||||
if (!tags || !tags.length) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
{tags.map(tag => {
|
|
||||||
return (
|
|
||||||
<span key={tag} onClick={e => this.onTagClick(e, tag)} className="pointer">
|
|
||||||
<TagBadge label={tag} removeIcon={true} count={0} />
|
|
||||||
</span>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const { options } = this.props;
|
|
||||||
const labelWidth = 8;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<PanelOptionsGrid>
|
|
||||||
<PanelOptionsGroup title="Display">
|
|
||||||
<Switch
|
|
||||||
label="Show User"
|
|
||||||
labelClass={`width-${labelWidth}`}
|
|
||||||
checked={options.showUser}
|
|
||||||
onChange={this.onToggleShowUser}
|
|
||||||
/>
|
|
||||||
<Switch
|
|
||||||
label="Show Time"
|
|
||||||
labelClass={`width-${labelWidth}`}
|
|
||||||
checked={options.showTime}
|
|
||||||
onChange={this.onToggleShowTime}
|
|
||||||
/>
|
|
||||||
<Switch
|
|
||||||
label="Show Tags"
|
|
||||||
labelClass={`width-${labelWidth}`}
|
|
||||||
checked={options.showTags}
|
|
||||||
onChange={this.onToggleShowTags}
|
|
||||||
/>
|
|
||||||
</PanelOptionsGroup>
|
|
||||||
<PanelOptionsGroup title="Navigate">
|
|
||||||
<FormField
|
|
||||||
label="Before"
|
|
||||||
labelWidth={labelWidth}
|
|
||||||
onChange={this.onNavigateBeforeChange}
|
|
||||||
value={options.navigateBefore}
|
|
||||||
/>
|
|
||||||
<FormField
|
|
||||||
label="After"
|
|
||||||
labelWidth={labelWidth}
|
|
||||||
onChange={this.onNavigateAfterChange}
|
|
||||||
value={options.navigateAfter}
|
|
||||||
/>
|
|
||||||
<Switch
|
|
||||||
label="To Panel"
|
|
||||||
labelClass={`width-${labelWidth}`}
|
|
||||||
checked={options.navigateToPanel}
|
|
||||||
onChange={this.onToggleNavigateToPanel}
|
|
||||||
/>
|
|
||||||
</PanelOptionsGroup>
|
|
||||||
<PanelOptionsGroup title="Search">
|
|
||||||
<Switch
|
|
||||||
label="Only This Dashboard"
|
|
||||||
labelClass={`width-12`}
|
|
||||||
checked={options.onlyFromThisDashboard}
|
|
||||||
onChange={this.onToggleOnlyFromThisDashboard}
|
|
||||||
/>
|
|
||||||
<Switch
|
|
||||||
label="Within Time Range"
|
|
||||||
labelClass={`width-12`}
|
|
||||||
checked={options.onlyInTimeRange}
|
|
||||||
onChange={this.onToggleOnlyInTimeRange}
|
|
||||||
/>
|
|
||||||
<div className="form-field">
|
|
||||||
<FormLabel width={6}>Tags</FormLabel>
|
|
||||||
{this.renderTags(options.tags)}
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
className={`gf-form-input width-${8}`}
|
|
||||||
value={this.state.tag}
|
|
||||||
onChange={this.onTagTextChange}
|
|
||||||
onKeyPress={ev => {
|
|
||||||
if (this.state.tag && ev.key === 'Enter') {
|
|
||||||
const tags = [...options.tags, this.state.tag];
|
|
||||||
this.props.onOptionsChange({
|
|
||||||
...this.props.options,
|
|
||||||
tags,
|
|
||||||
});
|
|
||||||
this.setState({ tag: '' });
|
|
||||||
ev.preventDefault();
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<FormField
|
|
||||||
label="Limit"
|
|
||||||
labelWidth={6}
|
|
||||||
onChange={this.onLimitChange}
|
|
||||||
value={toNumberString(options.limit)}
|
|
||||||
type="number"
|
|
||||||
/>
|
|
||||||
</PanelOptionsGroup>
|
|
||||||
</PanelOptionsGrid>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,16 +0,0 @@
|
|||||||
import { AnnoListPanel } from './AnnoListPanel';
|
|
||||||
import { AnnoOptions, defaults } from './types';
|
|
||||||
import { AnnoListEditor } from './AnnoListEditor';
|
|
||||||
import { PanelModel, PanelPlugin } from '@grafana/data';
|
|
||||||
|
|
||||||
export const plugin = new PanelPlugin<AnnoOptions>(AnnoListPanel)
|
|
||||||
.setDefaults(defaults)
|
|
||||||
.setEditor(AnnoListEditor)
|
|
||||||
|
|
||||||
// TODO, we should support this directly in the plugin infrastructure
|
|
||||||
.setPanelChangeHandler((panel: PanelModel<AnnoOptions>, prevPluginId: string, prevOptions: any) => {
|
|
||||||
if (prevPluginId === 'ryantxu-annolist-panel') {
|
|
||||||
return prevOptions as AnnoOptions;
|
|
||||||
}
|
|
||||||
return panel.options;
|
|
||||||
});
|
|
80
public/app/plugins/panel/annolist/module.tsx
Normal file
80
public/app/plugins/panel/annolist/module.tsx
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { PanelModel, PanelPlugin } from '@grafana/data';
|
||||||
|
import { TagsInput } from '@grafana/ui';
|
||||||
|
import { AnnoListPanel } from './AnnoListPanel';
|
||||||
|
import { AnnoOptions } from './types';
|
||||||
|
|
||||||
|
export const plugin = new PanelPlugin<AnnoOptions>(AnnoListPanel)
|
||||||
|
.setPanelOptions(builder => {
|
||||||
|
builder
|
||||||
|
.addBooleanSwitch({
|
||||||
|
path: 'showUser',
|
||||||
|
name: 'Show user',
|
||||||
|
description: '',
|
||||||
|
defaultValue: true,
|
||||||
|
})
|
||||||
|
.addBooleanSwitch({
|
||||||
|
path: 'showTime',
|
||||||
|
name: 'Show time',
|
||||||
|
description: '',
|
||||||
|
defaultValue: true,
|
||||||
|
})
|
||||||
|
.addBooleanSwitch({
|
||||||
|
path: 'showTags',
|
||||||
|
name: 'Show tags',
|
||||||
|
description: '',
|
||||||
|
defaultValue: true,
|
||||||
|
})
|
||||||
|
.addTextInput({
|
||||||
|
path: 'navigateBefore',
|
||||||
|
name: 'Before',
|
||||||
|
defaultValue: '10m',
|
||||||
|
description: '',
|
||||||
|
})
|
||||||
|
.addTextInput({
|
||||||
|
path: 'navigateAfter',
|
||||||
|
name: 'After',
|
||||||
|
defaultValue: '10m',
|
||||||
|
description: '',
|
||||||
|
})
|
||||||
|
.addBooleanSwitch({
|
||||||
|
path: 'navigateToPanel',
|
||||||
|
name: 'To panel',
|
||||||
|
description: '',
|
||||||
|
defaultValue: true,
|
||||||
|
})
|
||||||
|
.addBooleanSwitch({
|
||||||
|
path: 'onlyFromThisDashboard',
|
||||||
|
name: 'Only this dashboard',
|
||||||
|
description: '',
|
||||||
|
defaultValue: false,
|
||||||
|
})
|
||||||
|
.addBooleanSwitch({
|
||||||
|
path: 'onlyInTimeRange',
|
||||||
|
name: 'Within Time Range',
|
||||||
|
description: '',
|
||||||
|
defaultValue: false,
|
||||||
|
})
|
||||||
|
.addCustomEditor({
|
||||||
|
id: 'tags',
|
||||||
|
path: 'tags',
|
||||||
|
name: 'Tags',
|
||||||
|
description: '',
|
||||||
|
editor: props => {
|
||||||
|
return <TagsInput tags={props.value} onChange={props.onChange} />;
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.addNumberInput({
|
||||||
|
path: 'limit',
|
||||||
|
name: 'Limit',
|
||||||
|
description: '',
|
||||||
|
defaultValue: 10,
|
||||||
|
});
|
||||||
|
})
|
||||||
|
// TODO, we should support this directly in the plugin infrastructure
|
||||||
|
.setPanelChangeHandler((panel: PanelModel<AnnoOptions>, prevPluginId: string, prevOptions: any) => {
|
||||||
|
if (prevPluginId === 'ryantxu-annolist-panel') {
|
||||||
|
return prevOptions as AnnoOptions;
|
||||||
|
}
|
||||||
|
return panel.options;
|
||||||
|
});
|
@ -1,170 +0,0 @@
|
|||||||
// Libraries
|
|
||||||
import React, { PureComponent } from 'react';
|
|
||||||
|
|
||||||
import {
|
|
||||||
PanelOptionsGrid,
|
|
||||||
FieldDisplayEditor,
|
|
||||||
PanelOptionsGroup,
|
|
||||||
FormLabel,
|
|
||||||
LegacyForms,
|
|
||||||
FieldPropertiesEditor,
|
|
||||||
ThresholdsEditor,
|
|
||||||
LegacyValueMappingsEditor,
|
|
||||||
DataLinksEditor,
|
|
||||||
} from '@grafana/ui';
|
|
||||||
const { Select, Switch } = LegacyForms;
|
|
||||||
import {
|
|
||||||
DataLink,
|
|
||||||
FieldConfig,
|
|
||||||
ReduceDataOptions,
|
|
||||||
PanelEditorProps,
|
|
||||||
ThresholdsConfig,
|
|
||||||
ValueMapping,
|
|
||||||
} from '@grafana/data';
|
|
||||||
import { BarGaugeOptions, displayModes } from './types';
|
|
||||||
import { orientationOptions } from '../gauge/types';
|
|
||||||
import {
|
|
||||||
getCalculationValueDataLinksVariableSuggestions,
|
|
||||||
getDataLinksVariableSuggestions,
|
|
||||||
} from '../../../features/panel/panellinks/link_srv';
|
|
||||||
import { NewPanelEditorContext } from '../../../features/dashboard/components/PanelEditor/PanelEditor';
|
|
||||||
|
|
||||||
export class BarGaugePanelEditor extends PureComponent<PanelEditorProps<BarGaugeOptions>> {
|
|
||||||
onDisplayOptionsChanged = (fieldOptions: ReduceDataOptions) =>
|
|
||||||
this.props.onOptionsChange({
|
|
||||||
...this.props.options,
|
|
||||||
reduceOptions: fieldOptions,
|
|
||||||
});
|
|
||||||
|
|
||||||
onOrientationChange = ({ value }: any) => this.props.onOptionsChange({ ...this.props.options, orientation: value });
|
|
||||||
onDisplayModeChange = ({ value }: any) => this.props.onOptionsChange({ ...this.props.options, displayMode: value });
|
|
||||||
onToggleShowUnfilled = () => {
|
|
||||||
this.props.onOptionsChange({ ...this.props.options, showUnfilled: !this.props.options.showUnfilled });
|
|
||||||
};
|
|
||||||
|
|
||||||
onThresholdsChanged = (thresholds: ThresholdsConfig) => {
|
|
||||||
const current = this.props.fieldConfig;
|
|
||||||
this.props.onFieldConfigChange({
|
|
||||||
...current,
|
|
||||||
defaults: {
|
|
||||||
...current.defaults,
|
|
||||||
thresholds,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
onValueMappingsChanged = (mappings: ValueMapping[]) => {
|
|
||||||
const current = this.props.fieldConfig;
|
|
||||||
this.props.onFieldConfigChange({
|
|
||||||
...current,
|
|
||||||
defaults: {
|
|
||||||
...current.defaults,
|
|
||||||
mappings,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
onDataLinksChanged = (links: DataLink[]) => {
|
|
||||||
const current = this.props.fieldConfig;
|
|
||||||
this.props.onFieldConfigChange({
|
|
||||||
...current,
|
|
||||||
defaults: {
|
|
||||||
...current.defaults,
|
|
||||||
links,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
onDefaultsChange = (field: FieldConfig, event?: React.SyntheticEvent<HTMLElement>, callback?: () => void) => {
|
|
||||||
this.props.onFieldConfigChange({
|
|
||||||
...this.props.fieldConfig,
|
|
||||||
defaults: field,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const { options, fieldConfig } = this.props;
|
|
||||||
const { reduceOptions: fieldOptions } = options;
|
|
||||||
const { defaults } = fieldConfig;
|
|
||||||
|
|
||||||
const labelWidth = 6;
|
|
||||||
const suggestions = fieldOptions.values
|
|
||||||
? getDataLinksVariableSuggestions(this.props.data.series)
|
|
||||||
: getCalculationValueDataLinksVariableSuggestions(this.props.data.series);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<NewPanelEditorContext.Consumer>
|
|
||||||
{useNewEditor => {
|
|
||||||
if (useNewEditor) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<PanelOptionsGrid>
|
|
||||||
<PanelOptionsGroup title="Display">
|
|
||||||
<FieldDisplayEditor
|
|
||||||
onChange={this.onDisplayOptionsChanged}
|
|
||||||
value={fieldOptions}
|
|
||||||
labelWidth={labelWidth}
|
|
||||||
/>
|
|
||||||
<div className="form-field">
|
|
||||||
<FormLabel width={labelWidth}>Orientation</FormLabel>
|
|
||||||
<Select
|
|
||||||
width={12}
|
|
||||||
options={orientationOptions}
|
|
||||||
defaultValue={orientationOptions[0]}
|
|
||||||
onChange={this.onOrientationChange}
|
|
||||||
value={orientationOptions.find(item => item.value === options.orientation)}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className="form-field">
|
|
||||||
<FormLabel width={labelWidth}>Mode</FormLabel>
|
|
||||||
<Select
|
|
||||||
width={12}
|
|
||||||
options={displayModes}
|
|
||||||
defaultValue={displayModes[0]}
|
|
||||||
onChange={this.onDisplayModeChange}
|
|
||||||
value={displayModes.find(item => item.value === options.displayMode)}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<>
|
|
||||||
{options.displayMode !== 'lcd' && (
|
|
||||||
<Switch
|
|
||||||
label="Unfilled"
|
|
||||||
labelClass={`width-${labelWidth}`}
|
|
||||||
checked={options.showUnfilled}
|
|
||||||
onChange={this.onToggleShowUnfilled}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
</PanelOptionsGroup>
|
|
||||||
<PanelOptionsGroup title="Field">
|
|
||||||
<FieldPropertiesEditor
|
|
||||||
showMinMax={true}
|
|
||||||
showTitle={true}
|
|
||||||
onChange={this.onDefaultsChange}
|
|
||||||
value={defaults}
|
|
||||||
/>
|
|
||||||
</PanelOptionsGroup>
|
|
||||||
|
|
||||||
<ThresholdsEditor onChange={this.onThresholdsChanged} thresholds={defaults.thresholds} />
|
|
||||||
</PanelOptionsGrid>
|
|
||||||
|
|
||||||
<LegacyValueMappingsEditor onChange={this.onValueMappingsChanged} valueMappings={defaults.mappings} />
|
|
||||||
|
|
||||||
<PanelOptionsGroup title="Data links">
|
|
||||||
<DataLinksEditor
|
|
||||||
value={defaults.links}
|
|
||||||
onChange={this.onDataLinksChanged}
|
|
||||||
suggestions={suggestions}
|
|
||||||
maxLinks={10}
|
|
||||||
/>
|
|
||||||
</PanelOptionsGroup>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}}
|
|
||||||
</NewPanelEditorContext.Consumer>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,35 +1,29 @@
|
|||||||
import { sharedSingleStatPanelChangedHandler } from '@grafana/ui';
|
import { sharedSingleStatPanelChangedHandler } from '@grafana/ui';
|
||||||
import { PanelPlugin } from '@grafana/data';
|
import { PanelPlugin } from '@grafana/data';
|
||||||
import { BarGaugePanel } from './BarGaugePanel';
|
import { BarGaugePanel } from './BarGaugePanel';
|
||||||
import { BarGaugeOptions, defaults } from './types';
|
import { BarGaugeOptions, displayModes } from './types';
|
||||||
import { addStandardDataReduceOptions } from '../stat/types';
|
import { addStandardDataReduceOptions } from '../stat/types';
|
||||||
import { BarGaugePanelEditor } from './BarGaugePanelEditor';
|
|
||||||
import { barGaugePanelMigrationHandler } from './BarGaugeMigrations';
|
import { barGaugePanelMigrationHandler } from './BarGaugeMigrations';
|
||||||
|
|
||||||
export const plugin = new PanelPlugin<BarGaugeOptions>(BarGaugePanel)
|
export const plugin = new PanelPlugin<BarGaugeOptions>(BarGaugePanel)
|
||||||
.setDefaults(defaults)
|
|
||||||
.setEditor(BarGaugePanelEditor)
|
|
||||||
.useFieldConfig()
|
.useFieldConfig()
|
||||||
.setPanelOptions(builder => {
|
.setPanelOptions(builder => {
|
||||||
addStandardDataReduceOptions(builder);
|
addStandardDataReduceOptions(builder);
|
||||||
|
|
||||||
builder
|
builder
|
||||||
.addRadio({
|
.addRadio({
|
||||||
path: 'displayMode',
|
path: 'displayMode',
|
||||||
name: 'Display mode',
|
name: 'Display mode',
|
||||||
description: 'Controls the bar style',
|
description: 'Controls the bar style',
|
||||||
settings: {
|
settings: {
|
||||||
options: [
|
options: displayModes,
|
||||||
{ value: 'basic', label: 'Basic' },
|
|
||||||
{ value: 'gradient', label: 'Gradient' },
|
|
||||||
{ value: 'lcd', label: 'Retro LCD' },
|
|
||||||
],
|
|
||||||
},
|
},
|
||||||
|
defaultValue: 'lcd',
|
||||||
})
|
})
|
||||||
.addBooleanSwitch({
|
.addBooleanSwitch({
|
||||||
path: 'showUnfilled',
|
path: 'showUnfilled',
|
||||||
name: 'Show unfilled area',
|
name: 'Show unfilled area',
|
||||||
description: 'When enabled renders the unfilled region as gray',
|
description: 'When enabled renders the unfilled region as gray',
|
||||||
|
defaultValue: true,
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
.setPanelChangeHandler(sharedSingleStatPanelChangedHandler)
|
.setPanelChangeHandler(sharedSingleStatPanelChangedHandler)
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
import { SingleStatBaseOptions, BarGaugeDisplayMode } from '@grafana/ui';
|
import { SingleStatBaseOptions, BarGaugeDisplayMode } from '@grafana/ui';
|
||||||
import { commonValueOptionDefaults } from '../stat/types';
|
import { SelectableValue } from '@grafana/data';
|
||||||
import { VizOrientation, SelectableValue } from '@grafana/data';
|
|
||||||
|
|
||||||
export interface BarGaugeOptions extends SingleStatBaseOptions {
|
export interface BarGaugeOptions extends SingleStatBaseOptions {
|
||||||
displayMode: BarGaugeDisplayMode;
|
displayMode: BarGaugeDisplayMode;
|
||||||
@ -12,10 +11,3 @@ export const displayModes: Array<SelectableValue<string>> = [
|
|||||||
{ value: BarGaugeDisplayMode.Lcd, label: 'Retro LCD' },
|
{ value: BarGaugeDisplayMode.Lcd, label: 'Retro LCD' },
|
||||||
{ value: BarGaugeDisplayMode.Basic, label: 'Basic' },
|
{ value: BarGaugeDisplayMode.Basic, label: 'Basic' },
|
||||||
];
|
];
|
||||||
|
|
||||||
export const defaults: BarGaugeOptions = {
|
|
||||||
displayMode: BarGaugeDisplayMode.Lcd,
|
|
||||||
orientation: VizOrientation.Horizontal,
|
|
||||||
reduceOptions: commonValueOptionDefaults,
|
|
||||||
showUnfilled: true,
|
|
||||||
};
|
|
||||||
|
@ -1,163 +0,0 @@
|
|||||||
// Libraries
|
|
||||||
import React, { PureComponent } from 'react';
|
|
||||||
import {
|
|
||||||
PanelOptionsGrid,
|
|
||||||
FieldDisplayEditor,
|
|
||||||
LegacyForms,
|
|
||||||
PanelOptionsGroup,
|
|
||||||
FieldPropertiesEditor,
|
|
||||||
ThresholdsEditor,
|
|
||||||
LegacyValueMappingsEditor,
|
|
||||||
DataLinksEditor,
|
|
||||||
} from '@grafana/ui';
|
|
||||||
const { Switch } = LegacyForms;
|
|
||||||
import {
|
|
||||||
PanelEditorProps,
|
|
||||||
ReduceDataOptions,
|
|
||||||
ThresholdsConfig,
|
|
||||||
DataLink,
|
|
||||||
FieldConfig,
|
|
||||||
ValueMapping,
|
|
||||||
} from '@grafana/data';
|
|
||||||
|
|
||||||
import { GaugeOptions } from './types';
|
|
||||||
import {
|
|
||||||
getCalculationValueDataLinksVariableSuggestions,
|
|
||||||
getDataLinksVariableSuggestions,
|
|
||||||
} from '../../../features/panel/panellinks/link_srv';
|
|
||||||
import { NewPanelEditorContext } from '../../../features/dashboard/components/PanelEditor/PanelEditor';
|
|
||||||
|
|
||||||
export class GaugePanelEditor extends PureComponent<PanelEditorProps<GaugeOptions>> {
|
|
||||||
labelWidth = 6;
|
|
||||||
|
|
||||||
onToggleThresholdLabels = () =>
|
|
||||||
this.props.onOptionsChange({ ...this.props.options, showThresholdLabels: !this.props.options.showThresholdLabels });
|
|
||||||
|
|
||||||
onToggleThresholdMarkers = () =>
|
|
||||||
this.props.onOptionsChange({
|
|
||||||
...this.props.options,
|
|
||||||
showThresholdMarkers: !this.props.options.showThresholdMarkers,
|
|
||||||
});
|
|
||||||
|
|
||||||
onDisplayOptionsChanged = (
|
|
||||||
fieldOptions: ReduceDataOptions,
|
|
||||||
event?: React.SyntheticEvent<HTMLElement>,
|
|
||||||
callback?: () => void
|
|
||||||
) => {
|
|
||||||
this.props.onOptionsChange(
|
|
||||||
{
|
|
||||||
...this.props.options,
|
|
||||||
reduceOptions: fieldOptions,
|
|
||||||
},
|
|
||||||
callback
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
onThresholdsChanged = (thresholds: ThresholdsConfig) => {
|
|
||||||
const current = this.props.fieldConfig;
|
|
||||||
this.props.onFieldConfigChange({
|
|
||||||
...current,
|
|
||||||
defaults: {
|
|
||||||
...current.defaults,
|
|
||||||
thresholds,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
onValueMappingsChanged = (mappings: ValueMapping[]) => {
|
|
||||||
const current = this.props.fieldConfig;
|
|
||||||
this.props.onFieldConfigChange({
|
|
||||||
...current,
|
|
||||||
defaults: {
|
|
||||||
...current.defaults,
|
|
||||||
mappings,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
onDataLinksChanged = (links: DataLink[]) => {
|
|
||||||
const current = this.props.fieldConfig;
|
|
||||||
this.props.onFieldConfigChange({
|
|
||||||
...current,
|
|
||||||
defaults: {
|
|
||||||
...current.defaults,
|
|
||||||
links,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
onDefaultsChange = (field: FieldConfig) => {
|
|
||||||
this.props.onFieldConfigChange({
|
|
||||||
...this.props.fieldConfig,
|
|
||||||
defaults: field,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const { options, fieldConfig } = this.props;
|
|
||||||
const { showThresholdLabels, showThresholdMarkers, reduceOptions: valueOptions } = options;
|
|
||||||
|
|
||||||
const { defaults } = fieldConfig;
|
|
||||||
|
|
||||||
const suggestions = valueOptions.values
|
|
||||||
? getDataLinksVariableSuggestions(this.props.data.series)
|
|
||||||
: getCalculationValueDataLinksVariableSuggestions(this.props.data.series);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<NewPanelEditorContext.Consumer>
|
|
||||||
{useNewEditor => {
|
|
||||||
if (useNewEditor) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<PanelOptionsGrid>
|
|
||||||
<PanelOptionsGroup title="Display">
|
|
||||||
<FieldDisplayEditor
|
|
||||||
onChange={this.onDisplayOptionsChanged}
|
|
||||||
value={valueOptions}
|
|
||||||
labelWidth={this.labelWidth}
|
|
||||||
/>
|
|
||||||
<Switch
|
|
||||||
label="Labels"
|
|
||||||
labelClass={`width-${this.labelWidth}`}
|
|
||||||
checked={showThresholdLabels}
|
|
||||||
onChange={this.onToggleThresholdLabels}
|
|
||||||
/>
|
|
||||||
<Switch
|
|
||||||
label="Markers"
|
|
||||||
labelClass={`width-${this.labelWidth}`}
|
|
||||||
checked={showThresholdMarkers}
|
|
||||||
onChange={this.onToggleThresholdMarkers}
|
|
||||||
/>
|
|
||||||
</PanelOptionsGroup>
|
|
||||||
|
|
||||||
<PanelOptionsGroup title="Field">
|
|
||||||
<FieldPropertiesEditor
|
|
||||||
showMinMax={true}
|
|
||||||
showTitle={true}
|
|
||||||
onChange={this.onDefaultsChange}
|
|
||||||
value={defaults}
|
|
||||||
/>
|
|
||||||
</PanelOptionsGroup>
|
|
||||||
|
|
||||||
<ThresholdsEditor onChange={this.onThresholdsChanged} thresholds={defaults.thresholds} />
|
|
||||||
</PanelOptionsGrid>
|
|
||||||
|
|
||||||
<LegacyValueMappingsEditor onChange={this.onValueMappingsChanged} valueMappings={defaults.mappings} />
|
|
||||||
<PanelOptionsGroup title="Data links">
|
|
||||||
<DataLinksEditor
|
|
||||||
value={defaults.links}
|
|
||||||
onChange={this.onDataLinksChanged}
|
|
||||||
suggestions={suggestions}
|
|
||||||
maxLinks={10}
|
|
||||||
/>
|
|
||||||
</PanelOptionsGroup>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}}
|
|
||||||
</NewPanelEditorContext.Consumer>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,13 +1,10 @@
|
|||||||
import { PanelPlugin } from '@grafana/data';
|
import { PanelPlugin } from '@grafana/data';
|
||||||
import { GaugePanelEditor } from './GaugePanelEditor';
|
|
||||||
import { GaugePanel } from './GaugePanel';
|
import { GaugePanel } from './GaugePanel';
|
||||||
import { GaugeOptions, defaults } from './types';
|
import { GaugeOptions } from './types';
|
||||||
import { addStandardDataReduceOptions } from '../stat/types';
|
import { addStandardDataReduceOptions } 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)
|
|
||||||
.setEditor(GaugePanelEditor)
|
|
||||||
.useFieldConfig()
|
.useFieldConfig()
|
||||||
.setPanelOptions(builder => {
|
.setPanelOptions(builder => {
|
||||||
addStandardDataReduceOptions(builder);
|
addStandardDataReduceOptions(builder);
|
||||||
@ -16,11 +13,13 @@ export const plugin = new PanelPlugin<GaugeOptions>(GaugePanel)
|
|||||||
path: 'showThresholdLabels',
|
path: 'showThresholdLabels',
|
||||||
name: 'Show threshold Labels',
|
name: 'Show threshold Labels',
|
||||||
description: 'Render the threshold values around the gauge bar',
|
description: 'Render the threshold values around the gauge bar',
|
||||||
|
defaultValue: false,
|
||||||
})
|
})
|
||||||
.addBooleanSwitch({
|
.addBooleanSwitch({
|
||||||
path: 'showThresholdMarkers',
|
path: 'showThresholdMarkers',
|
||||||
name: 'Show threshold markers',
|
name: 'Show threshold markers',
|
||||||
description: 'Renders the thresholds as an outer bar',
|
description: 'Renders the thresholds as an outer bar',
|
||||||
|
defaultValue: true,
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
.setPanelChangeHandler(gaugePanelChangedHandler)
|
.setPanelChangeHandler(gaugePanelChangedHandler)
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
import { VizOrientation, SelectableValue } from '@grafana/data';
|
import { VizOrientation, SelectableValue } from '@grafana/data';
|
||||||
import { SingleStatBaseOptions } from '@grafana/ui/src/components/SingleStatShared/SingleStatBaseOptions';
|
import { SingleStatBaseOptions } from '@grafana/ui/src/components/SingleStatShared/SingleStatBaseOptions';
|
||||||
import { commonValueOptionDefaults } from '../stat/types';
|
|
||||||
|
|
||||||
export interface GaugeOptions extends SingleStatBaseOptions {
|
export interface GaugeOptions extends SingleStatBaseOptions {
|
||||||
showThresholdLabels: boolean;
|
showThresholdLabels: boolean;
|
||||||
@ -12,10 +11,3 @@ export const orientationOptions: Array<SelectableValue<VizOrientation>> = [
|
|||||||
{ value: VizOrientation.Horizontal, label: 'Horizontal' },
|
{ value: VizOrientation.Horizontal, label: 'Horizontal' },
|
||||||
{ value: VizOrientation.Vertical, label: 'Vertical' },
|
{ value: VizOrientation.Vertical, label: 'Vertical' },
|
||||||
];
|
];
|
||||||
|
|
||||||
export const defaults: GaugeOptions = {
|
|
||||||
showThresholdMarkers: true,
|
|
||||||
showThresholdLabels: false,
|
|
||||||
reduceOptions: commonValueOptionDefaults,
|
|
||||||
orientation: VizOrientation.Auto,
|
|
||||||
};
|
|
||||||
|
@ -1,105 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
import { LegendOptions, PanelOptionsGroup, LegacyForms, StatsPicker } from '@grafana/ui';
|
|
||||||
const { Input, Switch } = LegacyForms;
|
|
||||||
|
|
||||||
export interface GraphLegendEditorLegendOptions extends LegendOptions {
|
|
||||||
stats?: string[];
|
|
||||||
decimals?: number;
|
|
||||||
sortBy?: string;
|
|
||||||
sortDesc?: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface GraphLegendEditorProps {
|
|
||||||
options: GraphLegendEditorLegendOptions;
|
|
||||||
onChange: (options: GraphLegendEditorLegendOptions) => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const GraphLegendEditor: React.FunctionComponent<GraphLegendEditorProps> = props => {
|
|
||||||
const { options, onChange } = props;
|
|
||||||
|
|
||||||
const onStatsChanged = (stats: string[]) => {
|
|
||||||
onChange({
|
|
||||||
...options,
|
|
||||||
stats,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const onOptionToggle = (option: keyof LegendOptions) => (event?: React.ChangeEvent<HTMLInputElement>) => {
|
|
||||||
const newOption: Partial<LegendOptions> = {};
|
|
||||||
if (!event) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (option === 'placement') {
|
|
||||||
newOption[option] = event.target.checked ? 'right' : 'under';
|
|
||||||
} else {
|
|
||||||
newOption[option] = event.target.checked;
|
|
||||||
}
|
|
||||||
|
|
||||||
onChange({
|
|
||||||
...options,
|
|
||||||
...newOption,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const labelWidth = 8;
|
|
||||||
return (
|
|
||||||
<PanelOptionsGroup title="Legend">
|
|
||||||
<div className="section gf-form-group">
|
|
||||||
<h4>Options</h4>
|
|
||||||
<Switch
|
|
||||||
label="Show legend"
|
|
||||||
labelClass={`width-${labelWidth}`}
|
|
||||||
checked={options.isVisible}
|
|
||||||
onChange={onOptionToggle('isVisible')}
|
|
||||||
/>
|
|
||||||
<Switch
|
|
||||||
label="Display as table"
|
|
||||||
labelClass={`width-${labelWidth}`}
|
|
||||||
checked={options.asTable}
|
|
||||||
onChange={onOptionToggle('asTable')}
|
|
||||||
/>
|
|
||||||
<Switch
|
|
||||||
label="To the right"
|
|
||||||
labelClass={`width-${labelWidth}`}
|
|
||||||
checked={options.placement === 'right'}
|
|
||||||
onChange={onOptionToggle('placement')}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="section gf-form-group">
|
|
||||||
<h4>Show</h4>
|
|
||||||
<div className="gf-form">
|
|
||||||
<StatsPicker
|
|
||||||
allowMultiple={true}
|
|
||||||
stats={options.stats ? options.stats : []}
|
|
||||||
onChange={onStatsChanged}
|
|
||||||
placeholder={'Pick Values'}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="gf-form">
|
|
||||||
<div className="gf-form-label">Decimals</div>
|
|
||||||
<Input
|
|
||||||
className="gf-form-input width-5"
|
|
||||||
type="number"
|
|
||||||
value={options.decimals}
|
|
||||||
placeholder="Auto"
|
|
||||||
onChange={event => {
|
|
||||||
onChange({
|
|
||||||
...options,
|
|
||||||
decimals: parseInt(event.target.value, 10),
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="section gf-form-group">
|
|
||||||
<h4>Hidden series</h4>
|
|
||||||
{/* <Switch label="With only nulls" checked={!!options.hideEmpty} onChange={onOptionToggle('hideEmpty')} /> */}
|
|
||||||
<Switch label="With only zeros" checked={!!options.hideZero} onChange={onOptionToggle('hideZero')} />
|
|
||||||
</div>
|
|
||||||
</PanelOptionsGroup>
|
|
||||||
);
|
|
||||||
};
|
|
@ -42,7 +42,7 @@ export class GraphPanelController extends React.Component<GraphPanelControllerPr
|
|||||||
graphSeriesModel: getGraphSeriesModel(
|
graphSeriesModel: getGraphSeriesModel(
|
||||||
props.data.series,
|
props.data.series,
|
||||||
props.timeZone,
|
props.timeZone,
|
||||||
props.options.series,
|
props.options.series || {},
|
||||||
props.options.graph,
|
props.options.graph,
|
||||||
props.options.legend,
|
props.options.legend,
|
||||||
props.fieldConfig
|
props.fieldConfig
|
||||||
@ -56,7 +56,7 @@ export class GraphPanelController extends React.Component<GraphPanelControllerPr
|
|||||||
graphSeriesModel: getGraphSeriesModel(
|
graphSeriesModel: getGraphSeriesModel(
|
||||||
props.data.series,
|
props.data.series,
|
||||||
props.timeZone,
|
props.timeZone,
|
||||||
props.options.series,
|
props.options.series || {},
|
||||||
props.options.graph,
|
props.options.graph,
|
||||||
props.options.legend,
|
props.options.legend,
|
||||||
props.fieldConfig
|
props.fieldConfig
|
||||||
|
@ -1,107 +0,0 @@
|
|||||||
//// Libraries
|
|
||||||
import _ from 'lodash';
|
|
||||||
import React, { PureComponent } from 'react';
|
|
||||||
|
|
||||||
// Types
|
|
||||||
import { FieldConfig, PanelEditorProps } from '@grafana/data';
|
|
||||||
import {
|
|
||||||
LegendOptions,
|
|
||||||
GraphTooltipOptions,
|
|
||||||
PanelOptionsGrid,
|
|
||||||
PanelOptionsGroup,
|
|
||||||
LegacyForms,
|
|
||||||
FieldPropertiesEditor,
|
|
||||||
} from '@grafana/ui';
|
|
||||||
const { Select, Switch } = LegacyForms;
|
|
||||||
import { Options, GraphOptions } from './types';
|
|
||||||
import { GraphLegendEditor } from './GraphLegendEditor';
|
|
||||||
import { NewPanelEditorContext } from 'app/features/dashboard/components/PanelEditor/PanelEditor';
|
|
||||||
|
|
||||||
export class GraphPanelEditor extends PureComponent<PanelEditorProps<Options>> {
|
|
||||||
onGraphOptionsChange = (options: Partial<GraphOptions>) => {
|
|
||||||
this.props.onOptionsChange({
|
|
||||||
...this.props.options,
|
|
||||||
graph: {
|
|
||||||
...this.props.options.graph,
|
|
||||||
...options,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
onLegendOptionsChange = (options: LegendOptions) => {
|
|
||||||
this.props.onOptionsChange({ ...this.props.options, legend: options });
|
|
||||||
};
|
|
||||||
|
|
||||||
onTooltipOptionsChange = (options: GraphTooltipOptions) => {
|
|
||||||
this.props.onOptionsChange({ ...this.props.options, tooltipOptions: options });
|
|
||||||
};
|
|
||||||
|
|
||||||
onToggleLines = () => {
|
|
||||||
this.onGraphOptionsChange({ showLines: !this.props.options.graph.showLines });
|
|
||||||
};
|
|
||||||
|
|
||||||
onToggleBars = () => {
|
|
||||||
this.onGraphOptionsChange({ showBars: !this.props.options.graph.showBars });
|
|
||||||
};
|
|
||||||
|
|
||||||
onTogglePoints = () => {
|
|
||||||
this.onGraphOptionsChange({ showPoints: !this.props.options.graph.showPoints });
|
|
||||||
};
|
|
||||||
|
|
||||||
onDefaultsChange = (field: FieldConfig, event?: React.SyntheticEvent<HTMLElement>, callback?: () => void) => {
|
|
||||||
this.props.onFieldConfigChange({
|
|
||||||
...this.props.fieldConfig,
|
|
||||||
defaults: field,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const {
|
|
||||||
graph: { showBars, showPoints, showLines },
|
|
||||||
tooltipOptions: { mode },
|
|
||||||
} = this.props.options;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<NewPanelEditorContext.Consumer>
|
|
||||||
{useNewEditor => {
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<div className="section gf-form-group">
|
|
||||||
<h5 className="section-heading">Draw Modes</h5>
|
|
||||||
<Switch label="Lines" labelClass="width-5" checked={showLines} onChange={this.onToggleLines} />
|
|
||||||
<Switch label="Bars" labelClass="width-5" checked={showBars} onChange={this.onToggleBars} />
|
|
||||||
<Switch label="Points" labelClass="width-5" checked={showPoints} onChange={this.onTogglePoints} />
|
|
||||||
</div>
|
|
||||||
<PanelOptionsGrid>
|
|
||||||
<>
|
|
||||||
{!useNewEditor && (
|
|
||||||
<PanelOptionsGroup title="Field">
|
|
||||||
<FieldPropertiesEditor
|
|
||||||
showMinMax={false}
|
|
||||||
onChange={this.onDefaultsChange}
|
|
||||||
value={this.props.fieldConfig.defaults}
|
|
||||||
/>
|
|
||||||
</PanelOptionsGroup>
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
<PanelOptionsGroup title="Tooltip">
|
|
||||||
<Select
|
|
||||||
value={{ value: mode, label: mode === 'single' ? 'Single' : 'All series' }}
|
|
||||||
onChange={value => {
|
|
||||||
this.onTooltipOptionsChange({ mode: value.value as any });
|
|
||||||
}}
|
|
||||||
options={[
|
|
||||||
{ label: 'All series', value: 'multi' },
|
|
||||||
{ label: 'Single', value: 'single' },
|
|
||||||
]}
|
|
||||||
/>
|
|
||||||
</PanelOptionsGroup>
|
|
||||||
<GraphLegendEditor options={this.props.options.legend} onChange={this.onLegendOptionsChange} />
|
|
||||||
</PanelOptionsGrid>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}}
|
|
||||||
</NewPanelEditorContext.Consumer>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
@ -19,8 +19,7 @@ import {
|
|||||||
FieldConfigSource,
|
FieldConfigSource,
|
||||||
} from '@grafana/data';
|
} from '@grafana/data';
|
||||||
|
|
||||||
import { SeriesOptions, GraphOptions } from './types';
|
import { SeriesOptions, GraphOptions, GraphLegendEditorLegendOptions } from './types';
|
||||||
import { GraphLegendEditorLegendOptions } from './GraphLegendEditor';
|
|
||||||
|
|
||||||
export const getGraphSeriesModel = (
|
export const getGraphSeriesModel = (
|
||||||
dataFrames: DataFrame[],
|
dataFrames: DataFrame[],
|
||||||
|
@ -1,6 +1,63 @@
|
|||||||
import { PanelPlugin } from '@grafana/data';
|
import { FieldConfigProperty, PanelPlugin } from '@grafana/data';
|
||||||
import { GraphPanelEditor } from './GraphPanelEditor';
|
|
||||||
import { GraphPanel } from './GraphPanel';
|
import { GraphPanel } from './GraphPanel';
|
||||||
import { Options, defaults } from './types';
|
import { Options } from './types';
|
||||||
|
|
||||||
export const plugin = new PanelPlugin<Options>(GraphPanel).setDefaults(defaults).setEditor(GraphPanelEditor);
|
export const plugin = new PanelPlugin<Options>(GraphPanel)
|
||||||
|
.useFieldConfig({ standardOptions: [FieldConfigProperty.Unit, FieldConfigProperty.Decimals] })
|
||||||
|
.setPanelOptions(builder => {
|
||||||
|
builder
|
||||||
|
.addBooleanSwitch({
|
||||||
|
path: 'graph.showBars',
|
||||||
|
name: 'Show bars',
|
||||||
|
description: '',
|
||||||
|
defaultValue: false,
|
||||||
|
})
|
||||||
|
.addBooleanSwitch({
|
||||||
|
path: 'graph.showLines',
|
||||||
|
name: 'Show lines',
|
||||||
|
description: '',
|
||||||
|
defaultValue: true,
|
||||||
|
})
|
||||||
|
.addBooleanSwitch({
|
||||||
|
path: 'graph.showPoints',
|
||||||
|
name: 'Show poins',
|
||||||
|
description: '',
|
||||||
|
defaultValue: false,
|
||||||
|
})
|
||||||
|
.addBooleanSwitch({
|
||||||
|
path: 'legend.isVisible',
|
||||||
|
name: 'Show legend',
|
||||||
|
description: '',
|
||||||
|
defaultValue: true,
|
||||||
|
})
|
||||||
|
.addBooleanSwitch({
|
||||||
|
path: 'legend.asTable',
|
||||||
|
name: 'Display legend as table',
|
||||||
|
description: '',
|
||||||
|
defaultValue: false,
|
||||||
|
})
|
||||||
|
.addRadio({
|
||||||
|
path: 'legend.placement',
|
||||||
|
name: 'Legend placement',
|
||||||
|
description: '',
|
||||||
|
defaultValue: 'under',
|
||||||
|
settings: {
|
||||||
|
options: [
|
||||||
|
{ value: 'under', label: 'Below graph' },
|
||||||
|
{ value: 'right', label: 'Right to the graph' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.addRadio({
|
||||||
|
path: 'tooltipOptions.mode',
|
||||||
|
name: 'Tooltip mode',
|
||||||
|
description: '',
|
||||||
|
defaultValue: 'single',
|
||||||
|
settings: {
|
||||||
|
options: [
|
||||||
|
{ value: 'single', label: 'Single series' },
|
||||||
|
{ value: 'multi', label: 'All series' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
import { LegendOptions, GraphTooltipOptions } from '@grafana/ui';
|
import { LegendOptions, GraphTooltipOptions } from '@grafana/ui';
|
||||||
import { YAxis } from '@grafana/data';
|
import { YAxis } from '@grafana/data';
|
||||||
import { GraphLegendEditorLegendOptions } from './GraphLegendEditor';
|
|
||||||
|
|
||||||
export interface SeriesOptions {
|
export interface SeriesOptions {
|
||||||
color?: string;
|
color?: string;
|
||||||
@ -36,3 +35,10 @@ export const defaults: Options = {
|
|||||||
series: {},
|
series: {},
|
||||||
tooltipOptions: { mode: 'single' },
|
tooltipOptions: { mode: 'single' },
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export interface GraphLegendEditorLegendOptions extends LegendOptions {
|
||||||
|
stats?: string[];
|
||||||
|
decimals?: number;
|
||||||
|
sortBy?: string;
|
||||||
|
sortDesc?: boolean;
|
||||||
|
}
|
||||||
|
@ -112,4 +112,4 @@ export const getStyles = stylesFactory(() => {
|
|||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
export const plugin = new PanelPlugin(GrafanaLinksPanel).setDefaults({}).setNoPadding();
|
export const plugin = new PanelPlugin(GrafanaLinksPanel).setNoPadding();
|
||||||
|
@ -1,68 +0,0 @@
|
|||||||
// Libraries
|
|
||||||
import React, { PureComponent } from 'react';
|
|
||||||
import { PanelOptionsGrid, PanelOptionsGroup, FormLabel, LegacyForms } from '@grafana/ui';
|
|
||||||
const { Select, Switch } = LegacyForms;
|
|
||||||
|
|
||||||
// Types
|
|
||||||
import { Options } from './types';
|
|
||||||
import { SortOrder } from 'app/core/utils/explore';
|
|
||||||
import { PanelEditorProps, SelectableValue } from '@grafana/data';
|
|
||||||
|
|
||||||
const sortOrderOptions = [
|
|
||||||
{ value: SortOrder.Descending, label: 'Descending' },
|
|
||||||
{ value: SortOrder.Ascending, label: 'Ascending' },
|
|
||||||
];
|
|
||||||
|
|
||||||
export class LogsPanelEditor extends PureComponent<PanelEditorProps<Options>> {
|
|
||||||
onToggleLabels = () => {
|
|
||||||
const { options, onOptionsChange } = this.props;
|
|
||||||
const { showLabels } = options;
|
|
||||||
|
|
||||||
onOptionsChange({ ...options, showLabels: !showLabels });
|
|
||||||
};
|
|
||||||
|
|
||||||
onToggleTime = () => {
|
|
||||||
const { options, onOptionsChange } = this.props;
|
|
||||||
const { showTime } = options;
|
|
||||||
|
|
||||||
onOptionsChange({ ...options, showTime: !showTime });
|
|
||||||
};
|
|
||||||
|
|
||||||
onTogglewrapLogMessage = () => {
|
|
||||||
const { options, onOptionsChange } = this.props;
|
|
||||||
const { wrapLogMessage } = options;
|
|
||||||
|
|
||||||
onOptionsChange({ ...options, wrapLogMessage: !wrapLogMessage });
|
|
||||||
};
|
|
||||||
|
|
||||||
onShowValuesChange = (item: SelectableValue<SortOrder>) => {
|
|
||||||
const { options, onOptionsChange } = this.props;
|
|
||||||
onOptionsChange({ ...options, sortOrder: item.value });
|
|
||||||
};
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const { showLabels, showTime, wrapLogMessage, sortOrder } = this.props.options;
|
|
||||||
const value = sortOrderOptions.filter(option => option.value === sortOrder)[0];
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<PanelOptionsGrid>
|
|
||||||
<PanelOptionsGroup title="Columns">
|
|
||||||
<Switch label="Time" labelClass="width-10" checked={showTime} onChange={this.onToggleTime} />
|
|
||||||
<Switch label="Unique labels" labelClass="width-10" checked={showLabels} onChange={this.onToggleLabels} />
|
|
||||||
<Switch
|
|
||||||
label="Wrap lines"
|
|
||||||
labelClass="width-10"
|
|
||||||
checked={wrapLogMessage}
|
|
||||||
onChange={this.onTogglewrapLogMessage}
|
|
||||||
/>
|
|
||||||
<div className="gf-form">
|
|
||||||
<FormLabel>Order</FormLabel>
|
|
||||||
<Select options={sortOrderOptions} value={value} onChange={this.onShowValuesChange} />
|
|
||||||
</div>
|
|
||||||
</PanelOptionsGroup>
|
|
||||||
</PanelOptionsGrid>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,6 +1,38 @@
|
|||||||
import { PanelPlugin } from '@grafana/data';
|
import { PanelPlugin } from '@grafana/data';
|
||||||
import { Options, defaults } from './types';
|
import { Options } from './types';
|
||||||
import { LogsPanel } from './LogsPanel';
|
import { LogsPanel } from './LogsPanel';
|
||||||
import { LogsPanelEditor } from './LogsPanelEditor';
|
import { SortOrder } from '../../../core/utils/explore';
|
||||||
|
|
||||||
export const plugin = new PanelPlugin<Options>(LogsPanel).setDefaults(defaults).setEditor(LogsPanelEditor);
|
export const plugin = new PanelPlugin<Options>(LogsPanel).setPanelOptions(builder => {
|
||||||
|
builder
|
||||||
|
.addBooleanSwitch({
|
||||||
|
path: 'showTime',
|
||||||
|
name: 'Time',
|
||||||
|
description: '',
|
||||||
|
defaultValue: false,
|
||||||
|
})
|
||||||
|
.addBooleanSwitch({
|
||||||
|
path: 'showLabels',
|
||||||
|
name: 'Unique labels',
|
||||||
|
description: '',
|
||||||
|
defaultValue: false,
|
||||||
|
})
|
||||||
|
.addBooleanSwitch({
|
||||||
|
path: 'wrapLogMessage',
|
||||||
|
name: 'Wrap lines',
|
||||||
|
description: '',
|
||||||
|
defaultValue: false,
|
||||||
|
})
|
||||||
|
.addRadio({
|
||||||
|
path: 'sortOrder',
|
||||||
|
name: 'Order',
|
||||||
|
description: '',
|
||||||
|
settings: {
|
||||||
|
options: [
|
||||||
|
{ value: SortOrder.Descending, label: 'Descending' },
|
||||||
|
{ value: SortOrder.Ascending, label: 'Ascending' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
defaultValue: SortOrder.Descending,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
@ -6,10 +6,3 @@ export interface Options {
|
|||||||
wrapLogMessage: boolean;
|
wrapLogMessage: boolean;
|
||||||
sortOrder: SortOrder;
|
sortOrder: SortOrder;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const defaults: Options = {
|
|
||||||
showLabels: false,
|
|
||||||
showTime: true,
|
|
||||||
wrapLogMessage: true,
|
|
||||||
sortOrder: SortOrder.Descending,
|
|
||||||
};
|
|
||||||
|
@ -12,7 +12,8 @@ import { loadRSSFeed } from './rss';
|
|||||||
|
|
||||||
// Types
|
// Types
|
||||||
import { PanelProps, DataFrameView, dateTime } from '@grafana/data';
|
import { PanelProps, DataFrameView, dateTime } from '@grafana/data';
|
||||||
import { NewsOptions, NewsItem, DEFAULT_FEED_URL } from './types';
|
import { NewsOptions, NewsItem } from './types';
|
||||||
|
import { DEFAULT_FEED_URL, PROXY_PREFIX } from './constants';
|
||||||
|
|
||||||
interface Props extends PanelProps<NewsOptions> {}
|
interface Props extends PanelProps<NewsOptions> {}
|
||||||
|
|
||||||
@ -41,7 +42,11 @@ export class NewsPanel extends PureComponent<Props, State> {
|
|||||||
async loadFeed() {
|
async loadFeed() {
|
||||||
const { options } = this.props;
|
const { options } = this.props;
|
||||||
try {
|
try {
|
||||||
const url = options.feedUrl ?? DEFAULT_FEED_URL;
|
const url = options.feedUrl
|
||||||
|
? options.useProxy
|
||||||
|
? `${PROXY_PREFIX}${options.feedUrl}`
|
||||||
|
: options.feedUrl
|
||||||
|
: DEFAULT_FEED_URL;
|
||||||
const res = await loadRSSFeed(url);
|
const res = await loadRSSFeed(url);
|
||||||
const frame = feedToDataFrame(res);
|
const frame = feedToDataFrame(res);
|
||||||
this.setState({
|
this.setState({
|
||||||
|
@ -1,71 +0,0 @@
|
|||||||
import React, { PureComponent } from 'react';
|
|
||||||
import { FormField, PanelOptionsGroup, Button } from '@grafana/ui';
|
|
||||||
import { PanelEditorProps } from '@grafana/data';
|
|
||||||
import { NewsOptions, DEFAULT_FEED_URL } from './types';
|
|
||||||
|
|
||||||
const PROXY_PREFIX = 'https://cors-anywhere.herokuapp.com/';
|
|
||||||
|
|
||||||
interface State {
|
|
||||||
feedUrl?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export class NewsPanelEditor extends PureComponent<PanelEditorProps<NewsOptions>, State> {
|
|
||||||
constructor(props: PanelEditorProps<NewsOptions>) {
|
|
||||||
super(props);
|
|
||||||
|
|
||||||
this.state = {
|
|
||||||
feedUrl: props.options.feedUrl,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
onUpdatePanel = () =>
|
|
||||||
this.props.onOptionsChange({
|
|
||||||
...this.props.options,
|
|
||||||
feedUrl: this.state.feedUrl,
|
|
||||||
});
|
|
||||||
|
|
||||||
onFeedUrlChange = ({ target }: any) => this.setState({ feedUrl: target.value });
|
|
||||||
|
|
||||||
onSetProxyPrefix = () => {
|
|
||||||
const feedUrl = PROXY_PREFIX + this.state.feedUrl;
|
|
||||||
this.setState({ feedUrl });
|
|
||||||
this.props.onOptionsChange({
|
|
||||||
...this.props.options,
|
|
||||||
feedUrl,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const feedUrl = this.state.feedUrl || '';
|
|
||||||
const suggestProxy = feedUrl && !feedUrl.startsWith(PROXY_PREFIX);
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<PanelOptionsGroup title="Feed">
|
|
||||||
<>
|
|
||||||
<div className="gf-form">
|
|
||||||
<FormField
|
|
||||||
label="URL"
|
|
||||||
labelWidth={7}
|
|
||||||
inputWidth={30}
|
|
||||||
value={feedUrl || ''}
|
|
||||||
placeholder={DEFAULT_FEED_URL}
|
|
||||||
onChange={this.onFeedUrlChange}
|
|
||||||
tooltip="Only RSS feed formats are supported (not Atom)."
|
|
||||||
onBlur={this.onUpdatePanel}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
{suggestProxy && (
|
|
||||||
<div>
|
|
||||||
<br />
|
|
||||||
<div>If the feed is unable to connect, consider a CORS proxy</div>
|
|
||||||
<Button variant="secondary" onClick={this.onSetProxyPrefix}>
|
|
||||||
Use Proxy
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
</PanelOptionsGroup>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
2
public/app/plugins/panel/news/constants.ts
Normal file
2
public/app/plugins/panel/news/constants.ts
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
export const DEFAULT_FEED_URL = 'https://grafana.com/blog/news.xml';
|
||||||
|
export const PROXY_PREFIX = 'https://cors-anywhere.herokuapp.com/';
|
@ -1,6 +1,24 @@
|
|||||||
import { PanelPlugin } from '@grafana/data';
|
import { PanelPlugin } from '@grafana/data';
|
||||||
import { NewsPanel } from './NewsPanel';
|
import { NewsPanel } from './NewsPanel';
|
||||||
import { NewsPanelEditor } from './NewsPanelEditor';
|
import { NewsOptions } from './types';
|
||||||
import { defaults, NewsOptions } from './types';
|
import { DEFAULT_FEED_URL, PROXY_PREFIX } from './constants';
|
||||||
|
|
||||||
export const plugin = new PanelPlugin<NewsOptions>(NewsPanel).setDefaults(defaults).setEditor(NewsPanelEditor);
|
export const plugin = new PanelPlugin<NewsOptions>(NewsPanel).setPanelOptions(builder => {
|
||||||
|
builder
|
||||||
|
.addTextInput({
|
||||||
|
path: 'feedUrl',
|
||||||
|
name: 'URL',
|
||||||
|
description: 'Only RSS feed formats are supported (not Atom).',
|
||||||
|
settings: {
|
||||||
|
placeholder: DEFAULT_FEED_URL,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.addBooleanSwitch({
|
||||||
|
path: 'useProxy',
|
||||||
|
name: 'Use Proxy',
|
||||||
|
description: 'If the feed is unable to connect, consider a CORS proxy',
|
||||||
|
showIf: currentConfig => {
|
||||||
|
return currentConfig.feedUrl && !currentConfig.feedUrl.startsWith(PROXY_PREFIX);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
export const DEFAULT_FEED_URL = 'https://grafana.com/blog/news.xml';
|
|
||||||
|
|
||||||
export interface NewsOptions {
|
export interface NewsOptions {
|
||||||
feedUrl?: string;
|
feedUrl?: string;
|
||||||
|
useProxy?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const defaults: NewsOptions = {
|
export const defaults: NewsOptions = {
|
||||||
|
@ -1,67 +0,0 @@
|
|||||||
import React, { PureComponent } from 'react';
|
|
||||||
import {
|
|
||||||
PanelOptionsGrid,
|
|
||||||
FieldDisplayEditor,
|
|
||||||
PanelOptionsGroup,
|
|
||||||
FieldPropertiesEditor,
|
|
||||||
LegacyValueMappingsEditor,
|
|
||||||
} from '@grafana/ui';
|
|
||||||
import { PanelEditorProps, ReduceDataOptions, ValueMapping, FieldConfig } from '@grafana/data';
|
|
||||||
|
|
||||||
import { PieChartOptionsBox } from './PieChartOptionsBox';
|
|
||||||
import { PieChartOptions } from './types';
|
|
||||||
|
|
||||||
export class PieChartPanelEditor extends PureComponent<PanelEditorProps<PieChartOptions>> {
|
|
||||||
onValueMappingsChanged = (mappings: ValueMapping[]) => {
|
|
||||||
const current = this.props.fieldConfig;
|
|
||||||
this.props.onFieldConfigChange({
|
|
||||||
...current,
|
|
||||||
defaults: {
|
|
||||||
...current.defaults,
|
|
||||||
mappings,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
onDisplayOptionsChanged = (fieldOptions: ReduceDataOptions) =>
|
|
||||||
this.props.onOptionsChange({
|
|
||||||
...this.props.options,
|
|
||||||
reduceOptions: fieldOptions,
|
|
||||||
});
|
|
||||||
|
|
||||||
onDefaultsChange = (field: FieldConfig) => {
|
|
||||||
this.props.onFieldConfigChange({
|
|
||||||
...this.props.fieldConfig,
|
|
||||||
defaults: field,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const { onOptionsChange, options, data, fieldConfig, onFieldConfigChange } = this.props;
|
|
||||||
const { reduceOptions: fieldOptions } = options;
|
|
||||||
const { defaults } = fieldConfig;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<PanelOptionsGrid>
|
|
||||||
<PanelOptionsGroup title="Display">
|
|
||||||
<FieldDisplayEditor onChange={this.onDisplayOptionsChanged} value={fieldOptions} />
|
|
||||||
</PanelOptionsGroup>
|
|
||||||
|
|
||||||
<PanelOptionsGroup title="Field (default)">
|
|
||||||
<FieldPropertiesEditor showMinMax={true} onChange={this.onDefaultsChange} value={defaults} />
|
|
||||||
</PanelOptionsGroup>
|
|
||||||
|
|
||||||
<PieChartOptionsBox
|
|
||||||
data={data}
|
|
||||||
onOptionsChange={onOptionsChange}
|
|
||||||
options={options}
|
|
||||||
fieldConfig={fieldConfig}
|
|
||||||
onFieldConfigChange={onFieldConfigChange}
|
|
||||||
/>
|
|
||||||
</PanelOptionsGrid>
|
|
||||||
<LegacyValueMappingsEditor onChange={this.onValueMappingsChanged} valueMappings={defaults.mappings} />
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,8 +1,29 @@
|
|||||||
import { PanelPlugin } from '@grafana/data';
|
import { PanelPlugin } from '@grafana/data';
|
||||||
import { PieChartPanelEditor } from './PieChartPanelEditor';
|
|
||||||
import { PieChartPanel } from './PieChartPanel';
|
import { PieChartPanel } from './PieChartPanel';
|
||||||
import { PieChartOptions, defaults } from './types';
|
import { PieChartOptions } from './types';
|
||||||
|
import { addStandardDataReduceOptions } from '../stat/types';
|
||||||
|
import { PieChartType } from '@grafana/ui';
|
||||||
|
|
||||||
export const plugin = new PanelPlugin<PieChartOptions>(PieChartPanel)
|
export const plugin = new PanelPlugin<PieChartOptions>(PieChartPanel).setPanelOptions(builder => {
|
||||||
.setDefaults(defaults)
|
addStandardDataReduceOptions(builder, false);
|
||||||
.setEditor(PieChartPanelEditor);
|
|
||||||
|
builder
|
||||||
|
.addRadio({
|
||||||
|
name: 'Piechart type',
|
||||||
|
description: 'How the piechart should be rendered',
|
||||||
|
path: 'pieType',
|
||||||
|
settings: {
|
||||||
|
options: [
|
||||||
|
{ value: PieChartType.PIE, label: 'Pie' },
|
||||||
|
{ value: PieChartType.DONUT, label: 'Donut' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
defaultValue: PieChartType.PIE,
|
||||||
|
})
|
||||||
|
.addNumberInput({
|
||||||
|
name: 'Width',
|
||||||
|
description: 'Width of the piechart outline',
|
||||||
|
path: 'strokeWidth',
|
||||||
|
defaultValue: 1,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
@ -1,15 +1,6 @@
|
|||||||
import { PieChartType, SingleStatBaseOptions } from '@grafana/ui';
|
import { PieChartType, SingleStatBaseOptions } from '@grafana/ui';
|
||||||
import { commonValueOptionDefaults } from '../stat/types';
|
|
||||||
import { VizOrientation } from '@grafana/data';
|
|
||||||
|
|
||||||
export interface PieChartOptions extends SingleStatBaseOptions {
|
export interface PieChartOptions extends SingleStatBaseOptions {
|
||||||
pieType: PieChartType;
|
pieType: PieChartType;
|
||||||
strokeWidth: number;
|
strokeWidth: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const defaults: PieChartOptions = {
|
|
||||||
pieType: PieChartType.PIE,
|
|
||||||
strokeWidth: 1,
|
|
||||||
orientation: VizOrientation.Auto,
|
|
||||||
reduceOptions: commonValueOptionDefaults,
|
|
||||||
};
|
|
||||||
|
@ -1,177 +0,0 @@
|
|||||||
// Libraries
|
|
||||||
import React, { PureComponent } from 'react';
|
|
||||||
|
|
||||||
import {
|
|
||||||
PanelOptionsGrid,
|
|
||||||
FieldDisplayEditor,
|
|
||||||
PanelOptionsGroup,
|
|
||||||
FormLabel,
|
|
||||||
LegacyForms,
|
|
||||||
FieldPropertiesEditor,
|
|
||||||
ThresholdsEditor,
|
|
||||||
LegacyValueMappingsEditor,
|
|
||||||
DataLinksEditor,
|
|
||||||
} from '@grafana/ui';
|
|
||||||
|
|
||||||
const { Select } = LegacyForms;
|
|
||||||
|
|
||||||
import {
|
|
||||||
PanelEditorProps,
|
|
||||||
ReduceDataOptions,
|
|
||||||
FieldConfig,
|
|
||||||
ValueMapping,
|
|
||||||
ThresholdsConfig,
|
|
||||||
DataLink,
|
|
||||||
} from '@grafana/data';
|
|
||||||
|
|
||||||
import { StatPanelOptions, colorModes, graphModes, justifyModes } from './types';
|
|
||||||
import { orientationOptions } from '../gauge/types';
|
|
||||||
import {
|
|
||||||
getCalculationValueDataLinksVariableSuggestions,
|
|
||||||
getDataLinksVariableSuggestions,
|
|
||||||
} from '../../../features/panel/panellinks/link_srv';
|
|
||||||
import { NewPanelEditorContext } from '../../../features/dashboard/components/PanelEditor/PanelEditor';
|
|
||||||
|
|
||||||
export class StatPanelEditor extends PureComponent<PanelEditorProps<StatPanelOptions>> {
|
|
||||||
onThresholdsChanged = (thresholds: ThresholdsConfig) => {
|
|
||||||
const current = this.props.fieldConfig;
|
|
||||||
this.props.onFieldConfigChange({
|
|
||||||
...current,
|
|
||||||
defaults: {
|
|
||||||
...current.defaults,
|
|
||||||
thresholds,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
onValueMappingsChanged = (mappings: ValueMapping[]) => {
|
|
||||||
const current = this.props.fieldConfig;
|
|
||||||
this.props.onFieldConfigChange({
|
|
||||||
...current,
|
|
||||||
defaults: {
|
|
||||||
...current.defaults,
|
|
||||||
mappings,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
onDisplayOptionsChanged = (fieldOptions: ReduceDataOptions) =>
|
|
||||||
this.props.onOptionsChange({
|
|
||||||
...this.props.options,
|
|
||||||
reduceOptions: fieldOptions,
|
|
||||||
});
|
|
||||||
|
|
||||||
onColorModeChanged = ({ value }: any) => this.props.onOptionsChange({ ...this.props.options, colorMode: value });
|
|
||||||
onGraphModeChanged = ({ value }: any) => this.props.onOptionsChange({ ...this.props.options, graphMode: value });
|
|
||||||
onJustifyModeChanged = ({ value }: any) => this.props.onOptionsChange({ ...this.props.options, justifyMode: value });
|
|
||||||
onOrientationChange = ({ value }: any) => this.props.onOptionsChange({ ...this.props.options, orientation: value });
|
|
||||||
|
|
||||||
onDefaultsChange = (field: FieldConfig) => {
|
|
||||||
this.props.onFieldConfigChange({
|
|
||||||
...this.props.fieldConfig,
|
|
||||||
defaults: field,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
onDataLinksChanged = (links: DataLink[]) => {
|
|
||||||
const current = this.props.fieldConfig;
|
|
||||||
this.props.onFieldConfigChange({
|
|
||||||
...current,
|
|
||||||
defaults: {
|
|
||||||
...current.defaults,
|
|
||||||
links,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const { options, fieldConfig } = this.props;
|
|
||||||
const { reduceOptions: valueOptions } = options;
|
|
||||||
const { defaults } = fieldConfig;
|
|
||||||
|
|
||||||
const suggestions = valueOptions.values
|
|
||||||
? getDataLinksVariableSuggestions(this.props.data.series)
|
|
||||||
: getCalculationValueDataLinksVariableSuggestions(this.props.data.series);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<NewPanelEditorContext.Consumer>
|
|
||||||
{useNewEditor => {
|
|
||||||
if (useNewEditor) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<PanelOptionsGrid>
|
|
||||||
<PanelOptionsGroup title="Display">
|
|
||||||
<FieldDisplayEditor onChange={this.onDisplayOptionsChanged} value={valueOptions} labelWidth={8} />
|
|
||||||
<div className="form-field">
|
|
||||||
<FormLabel width={8}>Orientation</FormLabel>
|
|
||||||
<Select
|
|
||||||
width={12}
|
|
||||||
options={orientationOptions}
|
|
||||||
defaultValue={orientationOptions[0]}
|
|
||||||
onChange={this.onOrientationChange}
|
|
||||||
value={orientationOptions.find(item => item.value === options.orientation)}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className="form-field">
|
|
||||||
<FormLabel width={8}>Color</FormLabel>
|
|
||||||
<Select
|
|
||||||
width={12}
|
|
||||||
options={colorModes}
|
|
||||||
defaultValue={colorModes[0]}
|
|
||||||
onChange={this.onColorModeChanged}
|
|
||||||
value={colorModes.find(item => item.value === options.colorMode)}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className="form-field">
|
|
||||||
<FormLabel width={8}>Graph</FormLabel>
|
|
||||||
<Select
|
|
||||||
width={12}
|
|
||||||
options={graphModes}
|
|
||||||
defaultValue={graphModes[0]}
|
|
||||||
onChange={this.onGraphModeChanged}
|
|
||||||
value={graphModes.find(item => item.value === options.graphMode)}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className="form-field">
|
|
||||||
<FormLabel width={8}>Justify</FormLabel>
|
|
||||||
<Select
|
|
||||||
width={12}
|
|
||||||
options={justifyModes}
|
|
||||||
defaultValue={justifyModes[0]}
|
|
||||||
onChange={this.onJustifyModeChanged}
|
|
||||||
value={justifyModes.find(item => item.value === options.justifyMode)}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</PanelOptionsGroup>
|
|
||||||
|
|
||||||
<PanelOptionsGroup title="Field">
|
|
||||||
<FieldPropertiesEditor
|
|
||||||
showMinMax={true}
|
|
||||||
onChange={this.onDefaultsChange}
|
|
||||||
value={defaults}
|
|
||||||
showTitle={true}
|
|
||||||
/>
|
|
||||||
</PanelOptionsGroup>
|
|
||||||
<ThresholdsEditor onChange={this.onThresholdsChanged} thresholds={defaults.thresholds} />
|
|
||||||
</PanelOptionsGrid>
|
|
||||||
|
|
||||||
<LegacyValueMappingsEditor onChange={this.onValueMappingsChanged} valueMappings={defaults.mappings} />
|
|
||||||
|
|
||||||
<PanelOptionsGroup title="Data links">
|
|
||||||
<DataLinksEditor
|
|
||||||
value={defaults.links}
|
|
||||||
onChange={this.onDataLinksChanged}
|
|
||||||
suggestions={suggestions}
|
|
||||||
maxLinks={10}
|
|
||||||
/>
|
|
||||||
</PanelOptionsGroup>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}}
|
|
||||||
</NewPanelEditorContext.Consumer>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,12 +1,9 @@
|
|||||||
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, addStandardDataReduceOptions } from './types';
|
import { StatPanelOptions, addStandardDataReduceOptions } from './types';
|
||||||
import { StatPanel } from './StatPanel';
|
import { StatPanel } from './StatPanel';
|
||||||
import { StatPanelEditor } from './StatPanelEditor';
|
|
||||||
|
|
||||||
export const plugin = new PanelPlugin<StatPanelOptions>(StatPanel)
|
export const plugin = new PanelPlugin<StatPanelOptions>(StatPanel)
|
||||||
.setDefaults(defaults)
|
|
||||||
.setEditor(StatPanelEditor)
|
|
||||||
.useFieldConfig()
|
.useFieldConfig()
|
||||||
.setPanelOptions(builder => {
|
.setPanelOptions(builder => {
|
||||||
addStandardDataReduceOptions(builder);
|
addStandardDataReduceOptions(builder);
|
||||||
@ -16,6 +13,7 @@ export const plugin = new PanelPlugin<StatPanelOptions>(StatPanel)
|
|||||||
path: 'colorMode',
|
path: 'colorMode',
|
||||||
name: 'Color mode',
|
name: 'Color mode',
|
||||||
description: 'Color either the value or the background',
|
description: 'Color either the value or the background',
|
||||||
|
defaultValue: 'value',
|
||||||
settings: {
|
settings: {
|
||||||
options: [
|
options: [
|
||||||
{ value: 'value', label: 'Value' },
|
{ value: 'value', label: 'Value' },
|
||||||
@ -27,6 +25,7 @@ export const plugin = new PanelPlugin<StatPanelOptions>(StatPanel)
|
|||||||
path: 'graphMode',
|
path: 'graphMode',
|
||||||
name: 'Graph mode',
|
name: 'Graph mode',
|
||||||
description: 'Stat panel graph / sparkline mode',
|
description: 'Stat panel graph / sparkline mode',
|
||||||
|
defaultValue: 'area',
|
||||||
settings: {
|
settings: {
|
||||||
options: [
|
options: [
|
||||||
{ value: 'none', label: 'None' },
|
{ value: 'none', label: 'None' },
|
||||||
@ -38,6 +37,7 @@ export const plugin = new PanelPlugin<StatPanelOptions>(StatPanel)
|
|||||||
path: 'justifyMode',
|
path: 'justifyMode',
|
||||||
name: 'Justify mode',
|
name: 'Justify mode',
|
||||||
description: 'Value & title posititioning',
|
description: 'Value & title posititioning',
|
||||||
|
defaultValue: 'auto',
|
||||||
settings: {
|
settings: {
|
||||||
options: [
|
options: [
|
||||||
{ value: 'auto', label: 'Auto' },
|
{ value: 'auto', label: 'Auto' },
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { SingleStatBaseOptions, BigValueColorMode, BigValueGraphMode, BigValueJustifyMode } from '@grafana/ui';
|
import { SingleStatBaseOptions, BigValueColorMode, BigValueGraphMode, BigValueJustifyMode } from '@grafana/ui';
|
||||||
import { VizOrientation, ReducerID, ReduceDataOptions, SelectableValue, standardEditorsRegistry } from '@grafana/data';
|
import { ReducerID, SelectableValue, standardEditorsRegistry } from '@grafana/data';
|
||||||
import { PanelOptionsEditorBuilder } from '@grafana/data/src/utils/OptionsUIBuilders';
|
import { PanelOptionsEditorBuilder } from '@grafana/data/src/utils/OptionsUIBuilders';
|
||||||
|
|
||||||
// Structure copied from angular
|
// Structure copied from angular
|
||||||
@ -24,12 +24,10 @@ export const justifyModes: Array<SelectableValue<BigValueJustifyMode>> = [
|
|||||||
{ value: BigValueJustifyMode.Center, label: 'Center' },
|
{ value: BigValueJustifyMode.Center, label: 'Center' },
|
||||||
];
|
];
|
||||||
|
|
||||||
export const commonValueOptionDefaults: ReduceDataOptions = {
|
export function addStandardDataReduceOptions(
|
||||||
values: false,
|
builder: PanelOptionsEditorBuilder<SingleStatBaseOptions>,
|
||||||
calcs: [ReducerID.mean],
|
includeOrientation = true
|
||||||
};
|
) {
|
||||||
|
|
||||||
export function addStandardDataReduceOptions(builder: PanelOptionsEditorBuilder<StatPanelOptions>) {
|
|
||||||
builder.addRadio({
|
builder.addRadio({
|
||||||
path: 'reduceOptions.values',
|
path: 'reduceOptions.values',
|
||||||
name: 'Show',
|
name: 'Show',
|
||||||
@ -40,6 +38,7 @@ export function addStandardDataReduceOptions(builder: PanelOptionsEditorBuilder<
|
|||||||
{ value: true, label: 'All values' },
|
{ value: true, label: 'All values' },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
defaultValue: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
builder.addNumberInput({
|
builder.addNumberInput({
|
||||||
@ -60,26 +59,22 @@ export function addStandardDataReduceOptions(builder: PanelOptionsEditorBuilder<
|
|||||||
name: 'Value',
|
name: 'Value',
|
||||||
description: 'Choose a reducer function / calculation',
|
description: 'Choose a reducer function / calculation',
|
||||||
editor: standardEditorsRegistry.get('stats-picker').editor as any,
|
editor: standardEditorsRegistry.get('stats-picker').editor as any,
|
||||||
|
defaultValue: [ReducerID.mean],
|
||||||
});
|
});
|
||||||
|
|
||||||
builder.addRadio({
|
if (includeOrientation) {
|
||||||
path: 'orientation',
|
builder.addRadio({
|
||||||
name: 'Orientation',
|
path: 'orientation',
|
||||||
description: 'Stacking direction in case of multiple series or fields',
|
name: 'Orientation',
|
||||||
settings: {
|
description: 'Stacking direction in case of multiple series or fields',
|
||||||
options: [
|
settings: {
|
||||||
{ value: 'auto', label: 'Auto' },
|
options: [
|
||||||
{ value: 'horizontal', label: 'Horizontal' },
|
{ value: 'auto', label: 'Auto' },
|
||||||
{ value: 'vertical', label: 'Vertical' },
|
{ value: 'horizontal', label: 'Horizontal' },
|
||||||
],
|
{ value: 'vertical', label: 'Vertical' },
|
||||||
},
|
],
|
||||||
});
|
},
|
||||||
|
defaultValue: 'auto',
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const defaults: StatPanelOptions = {
|
|
||||||
graphMode: BigValueGraphMode.Area,
|
|
||||||
colorMode: BigValueColorMode.Value,
|
|
||||||
justifyMode: BigValueJustifyMode.Auto,
|
|
||||||
reduceOptions: commonValueOptionDefaults,
|
|
||||||
orientation: VizOrientation.Auto,
|
|
||||||
};
|
|
||||||
|
@ -1,26 +0,0 @@
|
|||||||
//// Libraries
|
|
||||||
import React, { PureComponent } from 'react';
|
|
||||||
// Types
|
|
||||||
import { PanelEditorProps } from '@grafana/data';
|
|
||||||
import { LegacyForms } from '@grafana/ui';
|
|
||||||
const { Switch } = LegacyForms;
|
|
||||||
import { Options } from './types';
|
|
||||||
|
|
||||||
export class TablePanelEditor extends PureComponent<PanelEditorProps<Options>> {
|
|
||||||
onToggleShowHeader = () => {
|
|
||||||
this.props.onOptionsChange({ ...this.props.options, showHeader: !this.props.options.showHeader });
|
|
||||||
};
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const { showHeader } = this.props.options;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<div className="section gf-form-group">
|
|
||||||
<h5 className="section-heading">Header</h5>
|
|
||||||
<Switch label="Show" labelClass="width-6" checked={showHeader} onChange={this.onToggleShowHeader} />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,9 +1,8 @@
|
|||||||
import { PanelPlugin } from '@grafana/data';
|
import { PanelPlugin } from '@grafana/data';
|
||||||
import { TablePanel } from './TablePanel';
|
import { TablePanel } from './TablePanel';
|
||||||
import { CustomFieldConfig, defaults, Options } from './types';
|
import { CustomFieldConfig, Options } from './types';
|
||||||
|
|
||||||
export const plugin = new PanelPlugin<Options, CustomFieldConfig>(TablePanel)
|
export const plugin = new PanelPlugin<Options, CustomFieldConfig>(TablePanel)
|
||||||
.setDefaults(defaults)
|
|
||||||
.useFieldConfig({
|
.useFieldConfig({
|
||||||
useCustomConfig: builder => {
|
useCustomConfig: builder => {
|
||||||
builder
|
builder
|
||||||
@ -47,16 +46,17 @@ export const plugin = new PanelPlugin<Options, CustomFieldConfig>(TablePanel)
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
.setPanelOptions(builder => {
|
.setPanelOptions(builder => {
|
||||||
builder.addBooleanSwitch({
|
builder
|
||||||
path: 'showHeader',
|
.addBooleanSwitch({
|
||||||
name: 'Show header',
|
path: 'showHeader',
|
||||||
description: "To display table's header or not to display",
|
name: 'Show header',
|
||||||
});
|
description: "To display table's header or not to display",
|
||||||
})
|
defaultValue: true,
|
||||||
.setPanelOptions(builder => {
|
})
|
||||||
builder.addBooleanSwitch({
|
.addBooleanSwitch({
|
||||||
path: 'resizable',
|
path: 'resizable',
|
||||||
name: 'Resizable',
|
name: 'Resizable',
|
||||||
description: 'Toggles if table columns are resizable or not',
|
description: 'Toggles if table columns are resizable or not',
|
||||||
});
|
defaultValue: false,
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@ -7,8 +7,3 @@ export interface CustomFieldConfig {
|
|||||||
width: number;
|
width: number;
|
||||||
displayMode: string;
|
displayMode: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const defaults: Options = {
|
|
||||||
showHeader: true,
|
|
||||||
resizable: false,
|
|
||||||
};
|
|
||||||
|
@ -1,41 +0,0 @@
|
|||||||
// Libraries
|
|
||||||
import React, { PureComponent, ChangeEvent } from 'react';
|
|
||||||
|
|
||||||
// Components
|
|
||||||
import { PanelOptionsGroup, LegacyForms } from '@grafana/ui';
|
|
||||||
const { Select } = LegacyForms;
|
|
||||||
import { PanelEditorProps, SelectableValue } from '@grafana/data';
|
|
||||||
|
|
||||||
// Types
|
|
||||||
import { TextOptions, TextMode } from './types';
|
|
||||||
|
|
||||||
export class TextPanelEditor extends PureComponent<PanelEditorProps<TextOptions>> {
|
|
||||||
modes: Array<SelectableValue<TextMode>> = [
|
|
||||||
{ value: 'markdown', label: 'Markdown' },
|
|
||||||
{ value: 'text', label: 'Text' },
|
|
||||||
{ value: 'html', label: 'HTML' },
|
|
||||||
];
|
|
||||||
|
|
||||||
onModeChange = (item: SelectableValue<TextMode>) =>
|
|
||||||
this.props.onOptionsChange({ ...this.props.options, mode: item.value! });
|
|
||||||
|
|
||||||
onContentChange = (evt: ChangeEvent<HTMLTextAreaElement>) => {
|
|
||||||
this.props.onOptionsChange({ ...this.props.options, content: (evt.target as any).value });
|
|
||||||
};
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const { mode, content } = this.props.options;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<PanelOptionsGroup title="Text">
|
|
||||||
<div className="gf-form-inline">
|
|
||||||
<div className="gf-form">
|
|
||||||
<span className="gf-form-label">Mode</span>
|
|
||||||
<Select onChange={this.onModeChange} value={this.modes.find(e => mode === e.value)} options={this.modes} />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<textarea value={content} onChange={this.onContentChange} className="gf-form-input" rows={10} />
|
|
||||||
</PanelOptionsGroup>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,12 +1,38 @@
|
|||||||
import { PanelModel, PanelPlugin } from '@grafana/data';
|
import { PanelModel, PanelPlugin } from '@grafana/data';
|
||||||
|
|
||||||
import { TextPanelEditor } from './TextPanelEditor';
|
|
||||||
import { TextPanel } from './TextPanel';
|
import { TextPanel } from './TextPanel';
|
||||||
import { TextOptions, defaults } from './types';
|
import { TextOptions } from './types';
|
||||||
|
|
||||||
export const plugin = new PanelPlugin<TextOptions>(TextPanel)
|
export const plugin = new PanelPlugin<TextOptions>(TextPanel)
|
||||||
.setDefaults(defaults)
|
.setPanelOptions(builder => {
|
||||||
.setEditor(TextPanelEditor)
|
builder
|
||||||
|
.addRadio({
|
||||||
|
path: 'mode',
|
||||||
|
name: 'Mode',
|
||||||
|
description: 'text mode of the panel',
|
||||||
|
settings: {
|
||||||
|
options: [
|
||||||
|
{ value: 'markdown', label: 'Markdown' },
|
||||||
|
{ value: 'text', label: 'Text' },
|
||||||
|
{ value: 'html', label: 'HTML' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
defaultValue: 'markdown',
|
||||||
|
})
|
||||||
|
.addTextInput({
|
||||||
|
path: 'content',
|
||||||
|
name: 'Content',
|
||||||
|
description: 'Content of the panel',
|
||||||
|
settings: {
|
||||||
|
useTextarea: true,
|
||||||
|
rows: 5,
|
||||||
|
},
|
||||||
|
defaultValue: `# Title
|
||||||
|
|
||||||
|
For markdown syntax help: [commonmark.org/help](https://commonmark.org/help/)
|
||||||
|
`,
|
||||||
|
});
|
||||||
|
})
|
||||||
.setPanelChangeHandler((panel: PanelModel<TextOptions>, prevPluginId: string, prevOptions: any) => {
|
.setPanelChangeHandler((panel: PanelModel<TextOptions>, prevPluginId: string, prevOptions: any) => {
|
||||||
if (prevPluginId === 'text') {
|
if (prevPluginId === 'text') {
|
||||||
return prevOptions as TextOptions;
|
return prevOptions as TextOptions;
|
||||||
|
@ -3,13 +3,3 @@ export interface TextOptions {
|
|||||||
mode: TextMode;
|
mode: TextMode;
|
||||||
content: string;
|
content: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const defaults: TextOptions = {
|
|
||||||
mode: 'markdown',
|
|
||||||
content: `# Title
|
|
||||||
|
|
||||||
For markdown syntax help: [commonmark.org/help](https://commonmark.org/help/)
|
|
||||||
|
|
||||||
|
|
||||||
`,
|
|
||||||
};
|
|
||||||
|
Loading…
Reference in New Issue
Block a user