Commit 53630b5f by Torkel Ödegaard Committed by GitHub

StatPanels: Refactoring DisplayValueOptions and renaming & adding new panel…

StatPanels: Refactoring DisplayValueOptions and renaming & adding new panel options to react panels  (#23153)

* StatPanels: Refactoring DisplayValueOptions and renaming

* added return

* Progress

* Updated

* Made radio groups full width by default in panel options

* Fixed ts issue

* Updated

* Added remaining options

* Removed unused type

* Updated snapshot

* Renamed to ReduceDataOptions
parent 8d2db9af
...@@ -26,7 +26,7 @@ describe('FieldDisplay', () => { ...@@ -26,7 +26,7 @@ describe('FieldDisplay', () => {
it('show first numeric values', () => { it('show first numeric values', () => {
const options = createDisplayOptions({ const options = createDisplayOptions({
fieldOptions: { reduceOptions: {
calcs: [ReducerID.first], calcs: [ReducerID.first],
}, },
fieldConfig: { fieldConfig: {
...@@ -42,7 +42,7 @@ describe('FieldDisplay', () => { ...@@ -42,7 +42,7 @@ describe('FieldDisplay', () => {
it('show last numeric values', () => { it('show last numeric values', () => {
const options = createDisplayOptions({ const options = createDisplayOptions({
fieldOptions: { reduceOptions: {
calcs: [ReducerID.last], calcs: [ReducerID.last],
}, },
}); });
...@@ -52,7 +52,7 @@ describe('FieldDisplay', () => { ...@@ -52,7 +52,7 @@ describe('FieldDisplay', () => {
it('show all numeric values', () => { it('show all numeric values', () => {
const options = createDisplayOptions({ const options = createDisplayOptions({
fieldOptions: { reduceOptions: {
values: true, // values: true, //
limit: 1000, limit: 1000,
calcs: [], calcs: [],
...@@ -64,7 +64,7 @@ describe('FieldDisplay', () => { ...@@ -64,7 +64,7 @@ describe('FieldDisplay', () => {
it('show 2 numeric values (limit)', () => { it('show 2 numeric values (limit)', () => {
const options = createDisplayOptions({ const options = createDisplayOptions({
fieldOptions: { reduceOptions: {
values: true, // values: true, //
limit: 2, limit: 2,
calcs: [], calcs: [],
...@@ -173,12 +173,8 @@ describe('FieldDisplay', () => { ...@@ -173,12 +173,8 @@ describe('FieldDisplay', () => {
}, },
]; ];
const options = createDisplayOptions({ const options = createDisplayOptions({
fieldOptions: { reduceOptions: {
calcs: [ReducerID.first], calcs: [ReducerID.first],
override: {},
defaults: {
mappings: mappingConfig,
},
}, },
}); });
...@@ -202,13 +198,9 @@ describe('FieldDisplay', () => { ...@@ -202,13 +198,9 @@ describe('FieldDisplay', () => {
}, },
]; ];
const options = createDisplayOptions({ const options = createDisplayOptions({
fieldOptions: { reduceOptions: {
calcs: [ReducerID.first], calcs: [ReducerID.first],
values: true, values: true,
override: {},
defaults: {
mappings: mappingConfig,
},
}, },
}); });
...@@ -253,7 +245,7 @@ function createDisplayOptions(extend: Partial<GetFieldDisplayValuesOptions> = {} ...@@ -253,7 +245,7 @@ function createDisplayOptions(extend: Partial<GetFieldDisplayValuesOptions> = {}
replaceVariables: (value: string) => { replaceVariables: (value: string) => {
return value; return value;
}, },
fieldOptions: { reduceOptions: {
calcs: [], calcs: [],
}, },
fieldConfig: { fieldConfig: {
......
...@@ -11,7 +11,6 @@ import { ...@@ -11,7 +11,6 @@ import {
FieldConfigSource, FieldConfigSource,
FieldType, FieldType,
InterpolateFunction, InterpolateFunction,
ValueMapping,
} from '../types'; } from '../types';
import { DataFrameView } from '../dataframe/DataFrameView'; import { DataFrameView } from '../dataframe/DataFrameView';
import { GraphSeriesValue } from '../types/graph'; import { GraphSeriesValue } from '../types/graph';
...@@ -20,15 +19,16 @@ import { reduceField, ReducerID } from '../transformations/fieldReducer'; ...@@ -20,15 +19,16 @@ import { reduceField, ReducerID } from '../transformations/fieldReducer';
import { ScopedVars } from '../types/ScopedVars'; import { ScopedVars } from '../types/ScopedVars';
import { getTimeField } from '../dataframe/processDataFrame'; import { getTimeField } from '../dataframe/processDataFrame';
// export interface FieldDisplayOptions extends FieldConfigSource { /**
export interface FieldDisplayOptions { * Options for how to turn DataFrames into an array of display values
values?: boolean; // If true show each row value */
limit?: number; // if showing all values limit export interface ReduceDataOptions {
calcs: string[]; // when !values, pick one value for the whole field /* If true show each row value */
override?: any; values?: boolean;
defaults?: { /** if showing all values limit */
mappings: ValueMapping[]; limit?: number;
}; /** When !values, pick one value for the whole field */
calcs: string[];
} }
// TODO: use built in variables, same as for data links? // TODO: use built in variables, same as for data links?
...@@ -81,7 +81,7 @@ export interface FieldDisplay { ...@@ -81,7 +81,7 @@ export interface FieldDisplay {
export interface GetFieldDisplayValuesOptions { export interface GetFieldDisplayValuesOptions {
data?: DataFrame[]; data?: DataFrame[];
fieldOptions: FieldDisplayOptions; reduceOptions: ReduceDataOptions;
fieldConfig: FieldConfigSource; fieldConfig: FieldConfigSource;
replaceVariables: InterpolateFunction; replaceVariables: InterpolateFunction;
sparkline?: boolean; // Calculate the sparkline sparkline?: boolean; // Calculate the sparkline
...@@ -92,8 +92,8 @@ export interface GetFieldDisplayValuesOptions { ...@@ -92,8 +92,8 @@ export interface GetFieldDisplayValuesOptions {
export const DEFAULT_FIELD_DISPLAY_VALUES_LIMIT = 25; export const DEFAULT_FIELD_DISPLAY_VALUES_LIMIT = 25;
export const getFieldDisplayValues = (options: GetFieldDisplayValuesOptions): FieldDisplay[] => { export const getFieldDisplayValues = (options: GetFieldDisplayValuesOptions): FieldDisplay[] => {
const { replaceVariables, fieldOptions, fieldConfig } = options; const { replaceVariables, reduceOptions, fieldConfig } = options;
const calcs = fieldOptions.calcs.length ? fieldOptions.calcs : [ReducerID.last]; const calcs = reduceOptions.calcs.length ? reduceOptions.calcs : [ReducerID.last];
const values: FieldDisplay[] = []; const values: FieldDisplay[] = [];
...@@ -101,7 +101,7 @@ export const getFieldDisplayValues = (options: GetFieldDisplayValuesOptions): Fi ...@@ -101,7 +101,7 @@ export const getFieldDisplayValues = (options: GetFieldDisplayValuesOptions): Fi
// Field overrides are applied already // Field overrides are applied already
const data = options.data; const data = options.data;
let hitLimit = false; let hitLimit = false;
const limit = fieldOptions.limit ? fieldOptions.limit : DEFAULT_FIELD_DISPLAY_VALUES_LIMIT; const limit = reduceOptions.limit ? reduceOptions.limit : DEFAULT_FIELD_DISPLAY_VALUES_LIMIT;
const defaultTitle = getTitleTemplate(fieldConfig.defaults.title, calcs, data); const defaultTitle = getTitleTemplate(fieldConfig.defaults.title, calcs, data);
const scopedVars: ScopedVars = {}; const scopedVars: ScopedVars = {};
...@@ -129,7 +129,7 @@ export const getFieldDisplayValues = (options: GetFieldDisplayValuesOptions): Fi ...@@ -129,7 +129,7 @@ export const getFieldDisplayValues = (options: GetFieldDisplayValuesOptions): Fi
const title = config.title ? config.title : defaultTitle; const title = config.title ? config.title : defaultTitle;
// Show all rows // Show all rows
if (fieldOptions.values) { if (reduceOptions.values) {
const usesCellValues = title.indexOf(VAR_CELL_PREFIX) >= 0; const usesCellValues = title.indexOf(VAR_CELL_PREFIX) >= 0;
for (let j = 0; j < field.values.length; j++) { for (let j = 0; j < field.values.length; j++) {
......
...@@ -13,8 +13,8 @@ const createTheme = (theme: GrafanaTheme) => { ...@@ -13,8 +13,8 @@ const createTheme = (theme: GrafanaTheme) => {
colorSecondary: theme.colors.brandPrimary, colorSecondary: theme.colors.brandPrimary,
// UI // UI
appBg: theme.colors.bodyBg, appBg: theme.colors.pageBg,
appContentBg: theme.colors.bodyBg, appContentBg: theme.colors.pageBg,
appBorderColor: theme.colors.pageHeaderBorder, appBorderColor: theme.colors.pageHeaderBorder,
appBorderRadius: 4, appBorderRadius: 4,
...@@ -29,7 +29,7 @@ const createTheme = (theme: GrafanaTheme) => { ...@@ -29,7 +29,7 @@ const createTheme = (theme: GrafanaTheme) => {
// Toolbar default and active colors // Toolbar default and active colors
barTextColor: theme.colors.formInputBorderActive, barTextColor: theme.colors.formInputBorderActive,
barSelectedColor: theme.colors.brandPrimary, barSelectedColor: theme.colors.brandPrimary,
barBg: theme.colors.bodyBg, barBg: theme.colors.pageBg,
// Form colors // Form colors
inputBg: theme.colors.formInputBg, inputBg: theme.colors.formInputBg,
......
...@@ -5,6 +5,7 @@ import { css, cx } from 'emotion'; ...@@ -5,6 +5,7 @@ import { css, cx } from 'emotion';
import { getFocusCss, getPropertiesForButtonSize } from '../commonStyles'; import { getFocusCss, getPropertiesForButtonSize } from '../commonStyles';
export type RadioButtonSize = 'sm' | 'md'; export type RadioButtonSize = 'sm' | 'md';
export interface RadioButtonProps { export interface RadioButtonProps {
size?: RadioButtonSize; size?: RadioButtonSize;
disabled?: boolean; disabled?: boolean;
...@@ -12,9 +13,10 @@ export interface RadioButtonProps { ...@@ -12,9 +13,10 @@ export interface RadioButtonProps {
active: boolean; active: boolean;
id: string; id: string;
onChange: () => void; onChange: () => void;
fullWidth?: boolean;
} }
const getRadioButtonStyles = stylesFactory((theme: GrafanaTheme, size: RadioButtonSize) => { const getRadioButtonStyles = stylesFactory((theme: GrafanaTheme, size: RadioButtonSize, fullWidth?: boolean) => {
const { fontSize, height } = getPropertiesForButtonSize(theme, size); const { fontSize, height } = getPropertiesForButtonSize(theme, size);
const horizontalPadding = theme.spacing[size] ?? theme.spacing.md; const horizontalPadding = theme.spacing[size] ?? theme.spacing.md;
const c = theme.colors; const c = theme.colors;
...@@ -79,6 +81,8 @@ const getRadioButtonStyles = stylesFactory((theme: GrafanaTheme, size: RadioButt ...@@ -79,6 +81,8 @@ const getRadioButtonStyles = stylesFactory((theme: GrafanaTheme, size: RadioButt
background: ${bg}; background: ${bg};
cursor: pointer; cursor: pointer;
z-index: 1; z-index: 1;
flex-grow: ${fullWidth ? 1 : 0};
text-align: center;
user-select: none; user-select: none;
...@@ -99,9 +103,10 @@ export const RadioButton: React.FC<RadioButtonProps> = ({ ...@@ -99,9 +103,10 @@ export const RadioButton: React.FC<RadioButtonProps> = ({
onChange, onChange,
id, id,
name = undefined, name = undefined,
fullWidth,
}) => { }) => {
const theme = useTheme(); const theme = useTheme();
const styles = getRadioButtonStyles(theme, size); const styles = getRadioButtonStyles(theme, size, fullWidth);
return ( return (
<> <>
......
...@@ -17,7 +17,7 @@ export default { ...@@ -17,7 +17,7 @@ export default {
const sizes: RadioButtonSize[] = ['sm', 'md']; const sizes: RadioButtonSize[] = ['sm', 'md'];
export const simple = () => { export const simple = () => {
const [selected, setSelected] = useState(); const [selected, setSelected] = useState('graphite');
const BEHAVIOUR_GROUP = 'Behaviour props'; const BEHAVIOUR_GROUP = 'Behaviour props';
const disabled = boolean('Disabled', false, BEHAVIOUR_GROUP); const disabled = boolean('Disabled', false, BEHAVIOUR_GROUP);
const disabledItem = select('Disabled item', ['', 'graphite', 'prometheus', 'elastic'], '', BEHAVIOUR_GROUP); const disabledItem = select('Disabled item', ['', 'graphite', 'prometheus', 'elastic'], '', BEHAVIOUR_GROUP);
...@@ -36,8 +36,37 @@ export const simple = () => { ...@@ -36,8 +36,37 @@ export const simple = () => {
disabled={disabled} disabled={disabled}
disabledOptions={[disabledItem]} disabledOptions={[disabledItem]}
value={selected} value={selected}
onChange={setSelected} onChange={v => setSelected(v!)}
size={size} size={size}
/> />
); );
}; };
export const fullWidth = () => {
const [selected, setSelected] = useState('elastic');
const BEHAVIOUR_GROUP = 'Behaviour props';
const disabled = boolean('Disabled', false, BEHAVIOUR_GROUP);
const disabledItem = select('Disabled item', ['', 'graphite', 'prometheus', 'elastic'], '', BEHAVIOUR_GROUP);
const VISUAL_GROUP = 'Visual options';
const size = select<RadioButtonSize>('Size', sizes, 'md', VISUAL_GROUP);
const options = [
{ label: 'Prometheus', value: 'prometheus' },
{ label: 'Graphite', value: 'graphite' },
{ label: 'Elastic', value: 'elastic' },
];
return (
<div style={{ width: '100%' }}>
<RadioButtonGroup
options={options}
disabled={disabled}
disabledOptions={[disabledItem]}
value={selected}
onChange={v => setSelected(v!)}
size={size}
fullWidth
/>
</div>
);
};
...@@ -39,6 +39,7 @@ interface RadioButtonGroupProps<T> { ...@@ -39,6 +39,7 @@ interface RadioButtonGroupProps<T> {
options: Array<SelectableValue<T>>; options: Array<SelectableValue<T>>;
onChange?: (value?: T) => void; onChange?: (value?: T) => void;
size?: RadioButtonSize; size?: RadioButtonSize;
fullWidth?: boolean;
} }
export function RadioButtonGroup<T>({ export function RadioButtonGroup<T>({
...@@ -48,6 +49,7 @@ export function RadioButtonGroup<T>({ ...@@ -48,6 +49,7 @@ export function RadioButtonGroup<T>({
disabled, disabled,
disabledOptions, disabledOptions,
size = 'md', size = 'md',
fullWidth,
}: RadioButtonGroupProps<T>) { }: RadioButtonGroupProps<T>) {
const handleOnChange = useCallback( const handleOnChange = useCallback(
(option: SelectableValue<T>) => { (option: SelectableValue<T>) => {
...@@ -75,6 +77,7 @@ export function RadioButtonGroup<T>({ ...@@ -75,6 +77,7 @@ export function RadioButtonGroup<T>({
onChange={handleOnChange(o)} onChange={handleOnChange(o)}
id={`option-${o.value}`} id={`option-${o.value}`}
name={groupName.current} name={groupName.current}
fullWidth
> >
{o.label} {o.label}
</RadioButton> </RadioButton>
......
import React from 'react';
import { FieldConfigEditorProps, ReducerID } from '@grafana/data';
import { StatsPicker } from '../StatsPicker/StatsPicker';
export const StatsPickerEditor: React.FC<FieldConfigEditorProps<string[], any>> = ({ value, onChange }) => {
return <StatsPicker stats={value} onChange={onChange} allowMultiple={false} defaultStat={ReducerID.mean} />;
};
...@@ -9,7 +9,7 @@ import { StatsPicker } from '../StatsPicker/StatsPicker'; ...@@ -9,7 +9,7 @@ import { StatsPicker } from '../StatsPicker/StatsPicker';
// Types // Types
import Select from '../Select/Select'; import Select from '../Select/Select';
import { import {
FieldDisplayOptions, ReduceDataOptions,
DEFAULT_FIELD_DISPLAY_VALUES_LIMIT, DEFAULT_FIELD_DISPLAY_VALUES_LIMIT,
ReducerID, ReducerID,
toNumberString, toNumberString,
...@@ -32,8 +32,8 @@ const showOptions: Array<SelectableValue<boolean>> = [ ...@@ -32,8 +32,8 @@ const showOptions: Array<SelectableValue<boolean>> = [
export interface Props { export interface Props {
labelWidth?: number; labelWidth?: number;
value: FieldDisplayOptions; value: ReduceDataOptions;
onChange: (value: FieldDisplayOptions, event?: React.SyntheticEvent<HTMLElement>) => void; onChange: (value: ReduceDataOptions, event?: React.SyntheticEvent<HTMLElement>) => void;
} }
export class FieldDisplayEditor extends PureComponent<Props> { export class FieldDisplayEditor extends PureComponent<Props> {
......
...@@ -11,7 +11,7 @@ import { ...@@ -11,7 +11,7 @@ import {
MappingType, MappingType,
VizOrientation, VizOrientation,
PanelModel, PanelModel,
FieldDisplayOptions, ReduceDataOptions,
ThresholdsMode, ThresholdsMode,
ThresholdsConfig, ThresholdsConfig,
validateFieldConfig, validateFieldConfig,
...@@ -19,11 +19,11 @@ import { ...@@ -19,11 +19,11 @@ import {
} from '@grafana/data'; } from '@grafana/data';
export interface SingleStatBaseOptions { export interface SingleStatBaseOptions {
fieldOptions: FieldDisplayOptions; reduceOptions: ReduceDataOptions;
orientation: VizOrientation; orientation: VizOrientation;
} }
const optionsToKeep = ['fieldOptions', 'orientation']; const optionsToKeep = ['reduceOptions', 'orientation'];
export function sharedSingleStatPanelChangedHandler( export function sharedSingleStatPanelChangedHandler(
panel: PanelModel<Partial<SingleStatBaseOptions>> | any, panel: PanelModel<Partial<SingleStatBaseOptions>> | any,
...@@ -131,9 +131,9 @@ export function sharedSingleStatMigrationHandler(panel: PanelModel<SingleStatBas ...@@ -131,9 +131,9 @@ export function sharedSingleStatMigrationHandler(panel: PanelModel<SingleStatBas
options = moveThresholdsAndMappingsToField(options); options = moveThresholdsAndMappingsToField(options);
} }
if (previousVersion < 6.6) { const { fieldOptions } = options;
const { fieldOptions } = options;
if (previousVersion < 6.6) {
// discard the old `override` options and enter an empty array // discard the old `override` options and enter an empty array
if (fieldOptions && fieldOptions.override) { if (fieldOptions && fieldOptions.override) {
const { override, ...rest } = options.fieldOptions; const { override, ...rest } = options.fieldOptions;
...@@ -178,16 +178,24 @@ export function sharedSingleStatMigrationHandler(panel: PanelModel<SingleStatBas ...@@ -178,16 +178,24 @@ export function sharedSingleStatMigrationHandler(panel: PanelModel<SingleStatBas
panel.fieldConfig = panel.fieldConfig || { defaults: {}, overrides: [] }; panel.fieldConfig = panel.fieldConfig || { defaults: {}, overrides: [] };
panel.fieldConfig = { panel.fieldConfig = {
defaults: defaults:
options.fieldOptions && options.fieldOptions.defaults fieldOptions && fieldOptions.defaults
? { ...panel.fieldConfig.defaults, ...options.fieldOptions.defaults } ? { ...panel.fieldConfig.defaults, ...fieldOptions.defaults }
: panel.fieldConfig.defaults, : panel.fieldConfig.defaults,
overrides: overrides:
options.fieldOptions && options.fieldOptions.overrides fieldOptions && fieldOptions.overrides
? [...panel.fieldConfig.overrides, ...options.fieldOptions.overrides] ? [...panel.fieldConfig.overrides, ...fieldOptions.overrides]
: panel.fieldConfig.overrides, : panel.fieldConfig.overrides,
}; };
delete options.fieldOptions.defaults;
delete options.fieldOptions.overrides; if (fieldOptions) {
options.reduceOptions = {
values: fieldOptions.values,
limit: fieldOptions.limit,
calcs: fieldOptions.calcs,
};
}
delete options.fieldOptions;
} }
return options as SingleStatBaseOptions; return options as SingleStatBaseOptions;
......
...@@ -3,7 +3,7 @@ import React, { PureComponent } from 'react'; ...@@ -3,7 +3,7 @@ import React, { PureComponent } from 'react';
import isArray from 'lodash/isArray'; import isArray from 'lodash/isArray';
import difference from 'lodash/difference'; import difference from 'lodash/difference';
import { Select } from '../Select/Select'; import { Select } from '../Forms/Select/Select';
import { fieldReducers, SelectableValue } from '@grafana/data'; import { fieldReducers, SelectableValue } from '@grafana/data';
...@@ -11,7 +11,6 @@ interface Props { ...@@ -11,7 +11,6 @@ interface Props {
placeholder?: string; placeholder?: string;
onChange: (stats: string[]) => void; onChange: (stats: string[]) => void;
stats: string[]; stats: string[];
width?: number;
allowMultiple?: boolean; allowMultiple?: boolean;
defaultStat?: string; defaultStat?: string;
} }
...@@ -63,12 +62,11 @@ export class StatsPicker extends PureComponent<Props> { ...@@ -63,12 +62,11 @@ export class StatsPicker extends PureComponent<Props> {
}; };
render() { render() {
const { width, stats, allowMultiple, defaultStat, placeholder } = this.props; const { stats, allowMultiple, defaultStat, placeholder } = this.props;
const select = fieldReducers.selectOptions(stats); const select = fieldReducers.selectOptions(stats);
return ( return (
<Select <Select
width={width}
value={select.current} value={select.current}
isClearable={!defaultStat} isClearable={!defaultStat}
isMulti={allowMultiple} isMulti={allowMultiple}
......
...@@ -24,6 +24,7 @@ import { ThresholdsValueEditor } from '../components/OptionsUI/thresholds'; ...@@ -24,6 +24,7 @@ import { ThresholdsValueEditor } from '../components/OptionsUI/thresholds';
import { UnitValueEditor } from '../components/OptionsUI/units'; import { UnitValueEditor } from '../components/OptionsUI/units';
import { DataLinksValueEditor } from '../components/OptionsUI/links'; import { DataLinksValueEditor } from '../components/OptionsUI/links';
import { ColorValueEditor } from '../components/OptionsUI/color'; import { ColorValueEditor } from '../components/OptionsUI/color';
import { StatsPickerEditor } from '../components/OptionsUI/stats';
/** /**
* Returns collection of common field config properties definitions * Returns collection of common field config properties definitions
...@@ -227,7 +228,7 @@ export const getStandardOptionEditors = () => { ...@@ -227,7 +228,7 @@ export const getStandardOptionEditors = () => {
id: 'radio', id: 'radio',
name: 'Radio', name: 'Radio',
description: 'Allows option selection', description: 'Allows option selection',
editor: props => <Forms.RadioButtonGroup {...props} options={props.item.settings?.options} />, editor: props => <Forms.RadioButtonGroup {...props} options={props.item.settings?.options} fullWidth />,
}; };
const unit: StandardEditorsRegistryItem<string> = { const unit: StandardEditorsRegistryItem<string> = {
...@@ -265,5 +266,12 @@ export const getStandardOptionEditors = () => { ...@@ -265,5 +266,12 @@ export const getStandardOptionEditors = () => {
editor: DataLinksValueEditor as any, editor: DataLinksValueEditor as any,
}; };
return [text, number, boolean, radio, select, unit, mappings, thresholds, links, color]; const statsPicker: StandardEditorsRegistryItem<string[]> = {
id: 'stats-picker',
name: 'Stats Picker',
editor: StatsPickerEditor as any,
description: '',
};
return [text, number, boolean, radio, select, unit, mappings, thresholds, links, color, statsPicker];
}; };
...@@ -65,10 +65,10 @@ export const OptionsPaneContent: React.FC<{ ...@@ -65,10 +65,10 @@ export const OptionsPaneContent: React.FC<{
const renderCustomPanelSettings = useCallback( const renderCustomPanelSettings = useCallback(
(plugin: PanelPlugin) => { (plugin: PanelPlugin) => {
const editors = []; const editors: JSX.Element[] = [];
if (plugin.editor && panel) { if (plugin.editor && panel) {
editors.push( editors.push(
<div className={styles.legacyOptions}> <div className={styles.legacyOptions} key="plugin custom panel settings">
<plugin.editor <plugin.editor
data={data} data={data}
options={panel.getOptions()} options={panel.getOptions()}
...@@ -83,12 +83,17 @@ export const OptionsPaneContent: React.FC<{ ...@@ -83,12 +83,17 @@ export const OptionsPaneContent: React.FC<{
// When editor created declaratively // When editor created declaratively
if (plugin.optionEditors && panel) { if (plugin.optionEditors && panel) {
editors.push( editors.push(
<PanelOptionsEditor options={panel.getOptions()} onChange={onPanelOptionsChanged} plugin={plugin} /> <PanelOptionsEditor
key="panel options"
options={panel.getOptions()}
onChange={onPanelOptionsChanged}
plugin={plugin}
/>
); );
} }
if (editors.length > 0) { if (editors.length > 0) {
return <>{editors}</>; return editors;
} }
return ( return (
......
...@@ -228,7 +228,7 @@ export class PanelEditorUnconnected extends PureComponent<Props> { ...@@ -228,7 +228,7 @@ export class PanelEditorUnconnected extends PureComponent<Props> {
const { plugin, dashboard, data, panel } = this.props; const { plugin, dashboard, data, panel } = this.props;
if (!plugin) { if (!plugin) {
return null; return <div />;
} }
return ( return (
......
import React, { useMemo } from 'react'; import React, { useMemo } from 'react';
import { set as lodashSet, get as lodashGet } from 'lodash';
import { PanelPlugin } from '@grafana/data'; import { PanelPlugin } from '@grafana/data';
import { Forms } from '@grafana/ui'; import { Forms } from '@grafana/ui';
...@@ -12,18 +13,16 @@ export const PanelOptionsEditor: React.FC<PanelOptionsEditorProps<any>> = ({ plu ...@@ -12,18 +13,16 @@ export const PanelOptionsEditor: React.FC<PanelOptionsEditorProps<any>> = ({ plu
const optionEditors = useMemo(() => plugin.optionEditors, [plugin]); const optionEditors = useMemo(() => plugin.optionEditors, [plugin]);
const onOptionChange = (key: string, value: any) => { const onOptionChange = (key: string, value: any) => {
onChange({ const newOptions = lodashSet({ ...options }, key, value);
...options, onChange(newOptions);
[key]: value,
});
}; };
return ( return (
<> <>
{optionEditors.list().map(e => { {optionEditors.list().map(e => {
return ( return (
<Forms.Field label={e.name} description={e.description}> <Forms.Field label={e.name} description={e.description} key={e.id}>
<e.editor value={options[e.id]} onChange={value => onOptionChange(e.id, value)} item={e} /> <e.editor value={lodashGet(options, e.id)} onChange={value => onOptionChange(e.id, value)} item={e} />
</Forms.Field> </Forms.Field>
); );
})} })}
......
...@@ -45,7 +45,9 @@ describe('BarGauge Panel Migrations', () => { ...@@ -45,7 +45,9 @@ describe('BarGauge Panel Migrations', () => {
type: 'bargauge', type: 'bargauge',
} as Omit<PanelModel, 'fieldConfig'>; } as Omit<PanelModel, 'fieldConfig'>;
expect(barGaugePanelMigrationHandler(panel as PanelModel)).toMatchSnapshot(); const newOptions = barGaugePanelMigrationHandler(panel as PanelModel);
// should mutate panel model and move field config out of panel.options
expect((panel as any).fieldConfig).toMatchInlineSnapshot(` expect((panel as any).fieldConfig).toMatchInlineSnapshot(`
Object { Object {
"defaults": Object { "defaults": Object {
...@@ -81,5 +83,20 @@ describe('BarGauge Panel Migrations', () => { ...@@ -81,5 +83,20 @@ describe('BarGauge Panel Migrations', () => {
"overrides": Array [], "overrides": Array [],
} }
`); `);
// should options options
expect(newOptions).toMatchInlineSnapshot(`
Object {
"displayMode": "lcd",
"orientation": "vertical",
"reduceOptions": Object {
"calcs": Array [
"mean",
],
"limit": undefined,
"values": false,
},
}
`);
}); });
}); });
...@@ -65,7 +65,7 @@ function createBarGaugePanelWithData(data: PanelData): ReactWrapper<PanelProps<B ...@@ -65,7 +65,7 @@ function createBarGaugePanelWithData(data: PanelData): ReactWrapper<PanelProps<B
const options: BarGaugeOptions = { const options: BarGaugeOptions = {
displayMode: BarGaugeDisplayMode.Lcd, displayMode: BarGaugeDisplayMode.Lcd,
fieldOptions: { reduceOptions: {
calcs: ['mean'], calcs: ['mean'],
values: false, values: false,
}, },
......
...@@ -50,7 +50,7 @@ export class BarGaugePanel extends PureComponent<PanelProps<BarGaugeOptions>> { ...@@ -50,7 +50,7 @@ export class BarGaugePanel extends PureComponent<PanelProps<BarGaugeOptions>> {
const { data, options, replaceVariables, fieldConfig } = this.props; const { data, options, replaceVariables, fieldConfig } = this.props;
return getFieldDisplayValues({ return getFieldDisplayValues({
fieldConfig, fieldConfig,
fieldOptions: options.fieldOptions, reduceOptions: options.reduceOptions,
replaceVariables, replaceVariables,
theme: config.theme, theme: config.theme,
data: data.series, data: data.series,
......
...@@ -16,7 +16,7 @@ import { ...@@ -16,7 +16,7 @@ import {
import { import {
DataLink, DataLink,
FieldConfig, FieldConfig,
FieldDisplayOptions, ReduceDataOptions,
PanelEditorProps, PanelEditorProps,
ThresholdsConfig, ThresholdsConfig,
ValueMapping, ValueMapping,
...@@ -30,10 +30,10 @@ import { ...@@ -30,10 +30,10 @@ import {
import { NewPanelEditorContext } from '../../../features/dashboard/components/PanelEditor/PanelEditor'; import { NewPanelEditorContext } from '../../../features/dashboard/components/PanelEditor/PanelEditor';
export class BarGaugePanelEditor extends PureComponent<PanelEditorProps<BarGaugeOptions>> { export class BarGaugePanelEditor extends PureComponent<PanelEditorProps<BarGaugeOptions>> {
onDisplayOptionsChanged = (fieldOptions: FieldDisplayOptions) => onDisplayOptionsChanged = (fieldOptions: ReduceDataOptions) =>
this.props.onOptionsChange({ this.props.onOptionsChange({
...this.props.options, ...this.props.options,
fieldOptions, reduceOptions: fieldOptions,
}); });
onOrientationChange = ({ value }: any) => this.props.onOptionsChange({ ...this.props.options, orientation: value }); onOrientationChange = ({ value }: any) => this.props.onOptionsChange({ ...this.props.options, orientation: value });
...@@ -84,7 +84,7 @@ export class BarGaugePanelEditor extends PureComponent<PanelEditorProps<BarGauge ...@@ -84,7 +84,7 @@ export class BarGaugePanelEditor extends PureComponent<PanelEditorProps<BarGauge
render() { render() {
const { options, fieldConfig } = this.props; const { options, fieldConfig } = this.props;
const { fieldOptions } = options; const { reduceOptions: fieldOptions } = options;
const { defaults } = fieldConfig; const { defaults } = fieldConfig;
const labelWidth = 6; const labelWidth = 6;
......
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`BarGauge Panel Migrations from 6.2 1`] = `
Object {
"displayMode": "lcd",
"fieldOptions": Object {
"calcs": Array [
"mean",
],
"thresholds": Array [
Object {
"color": "green",
"index": 0,
"value": -Infinity,
},
Object {
"color": "orange",
"index": 1,
"value": 40,
},
Object {
"color": "red",
"index": 2,
"value": 80,
},
],
"values": false,
},
"orientation": "vertical",
}
`;
...@@ -2,7 +2,7 @@ import { sharedSingleStatPanelChangedHandler } from '@grafana/ui'; ...@@ -2,7 +2,7 @@ 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, defaults } from './types';
import { standardFieldConfig } from '../stat/types'; import { standardFieldConfig, addStandardDataReduceOptions } from '../stat/types';
import { BarGaugePanelEditor } from './BarGaugePanelEditor'; import { BarGaugePanelEditor } from './BarGaugePanelEditor';
import { barGaugePanelMigrationHandler } from './BarGaugeMigrations'; import { barGaugePanelMigrationHandler } from './BarGaugeMigrations';
...@@ -11,20 +11,26 @@ export const plugin = new PanelPlugin<BarGaugeOptions>(BarGaugePanel) ...@@ -11,20 +11,26 @@ export const plugin = new PanelPlugin<BarGaugeOptions>(BarGaugePanel)
.setEditor(BarGaugePanelEditor) .setEditor(BarGaugePanelEditor)
.setFieldConfigDefaults(standardFieldConfig) .setFieldConfigDefaults(standardFieldConfig)
.setPanelOptions(builder => { .setPanelOptions(builder => {
/* addStandardSingleValueOptions(builder); */ addStandardDataReduceOptions(builder);
builder.addRadio({ builder
id: 'orientation', .addRadio({
name: 'Orientation', id: 'displayMode',
description: 'Stacking direction for multiple bars', name: 'Display mode',
settings: { description: 'Controls the bar style',
options: [ settings: {
{ value: 'auto', label: 'Auto' }, options: [
{ value: 'horizontal', label: 'Horizontal' }, { value: 'basic', label: 'Basic' },
{ value: 'vertical', label: 'Vertical' }, { value: 'gradient', label: 'Gradient' },
], { value: 'lcd', label: 'Retro LCD' },
}, ],
}); },
})
.addBooleanSwitch({
id: 'showUnfilled',
name: 'Show unfilled area',
description: 'When enabled renders the unfilled region as gray',
});
}) })
.setPanelChangeHandler(sharedSingleStatPanelChangedHandler) .setPanelChangeHandler(sharedSingleStatPanelChangedHandler)
.setMigrationHandler(barGaugePanelMigrationHandler); .setMigrationHandler(barGaugePanelMigrationHandler);
import { SingleStatBaseOptions, BarGaugeDisplayMode } from '@grafana/ui'; import { SingleStatBaseOptions, BarGaugeDisplayMode } from '@grafana/ui';
import { standardGaugeFieldOptions } from '../gauge/types'; import { commonValueOptionDefaults } from '../stat/types';
import { VizOrientation, SelectableValue } from '@grafana/data'; import { VizOrientation, SelectableValue } from '@grafana/data';
export interface BarGaugeOptions extends SingleStatBaseOptions { export interface BarGaugeOptions extends SingleStatBaseOptions {
...@@ -16,6 +16,6 @@ export const displayModes: Array<SelectableValue<string>> = [ ...@@ -16,6 +16,6 @@ export const displayModes: Array<SelectableValue<string>> = [
export const defaults: BarGaugeOptions = { export const defaults: BarGaugeOptions = {
displayMode: BarGaugeDisplayMode.Lcd, displayMode: BarGaugeDisplayMode.Lcd,
orientation: VizOrientation.Horizontal, orientation: VizOrientation.Horizontal,
fieldOptions: standardGaugeFieldOptions, reduceOptions: commonValueOptionDefaults,
showUnfilled: true, showUnfilled: true,
}; };
...@@ -82,10 +82,10 @@ describe('Gauge Panel Migrations', () => { ...@@ -82,10 +82,10 @@ describe('Gauge Panel Migrations', () => {
// Ignored due to the API change // Ignored due to the API change
//@ts-ignore //@ts-ignore
expect(result.fieldOptions.defaults).toBeUndefined(); expect(result.reduceOptions.defaults).toBeUndefined();
// Ignored due to the API change // Ignored due to the API change
//@ts-ignore //@ts-ignore
expect(result.fieldOptions.overrides).toBeUndefined(); expect(result.reduceOptions.overrides).toBeUndefined();
expect((panel as PanelModel).fieldConfig).toMatchInlineSnapshot(` expect((panel as PanelModel).fieldConfig).toMatchInlineSnapshot(`
Object { Object {
......
...@@ -43,7 +43,7 @@ export class GaugePanel extends PureComponent<PanelProps<GaugeOptions>> { ...@@ -43,7 +43,7 @@ export class GaugePanel extends PureComponent<PanelProps<GaugeOptions>> {
const { data, options, replaceVariables, fieldConfig } = this.props; const { data, options, replaceVariables, fieldConfig } = this.props;
return getFieldDisplayValues({ return getFieldDisplayValues({
fieldConfig, fieldConfig,
fieldOptions: options.fieldOptions, reduceOptions: options.reduceOptions,
replaceVariables, replaceVariables,
theme: config.theme, theme: config.theme,
data: data.series, data: data.series,
......
...@@ -12,7 +12,7 @@ import { ...@@ -12,7 +12,7 @@ import {
} from '@grafana/ui'; } from '@grafana/ui';
import { import {
PanelEditorProps, PanelEditorProps,
FieldDisplayOptions, ReduceDataOptions,
ThresholdsConfig, ThresholdsConfig,
DataLink, DataLink,
FieldConfig, FieldConfig,
...@@ -39,14 +39,14 @@ export class GaugePanelEditor extends PureComponent<PanelEditorProps<GaugeOption ...@@ -39,14 +39,14 @@ export class GaugePanelEditor extends PureComponent<PanelEditorProps<GaugeOption
}); });
onDisplayOptionsChanged = ( onDisplayOptionsChanged = (
fieldOptions: FieldDisplayOptions, fieldOptions: ReduceDataOptions,
event?: React.SyntheticEvent<HTMLElement>, event?: React.SyntheticEvent<HTMLElement>,
callback?: () => void callback?: () => void
) => { ) => {
this.props.onOptionsChange( this.props.onOptionsChange(
{ {
...this.props.options, ...this.props.options,
fieldOptions, reduceOptions: fieldOptions,
}, },
callback callback
); );
...@@ -94,24 +94,28 @@ export class GaugePanelEditor extends PureComponent<PanelEditorProps<GaugeOption ...@@ -94,24 +94,28 @@ export class GaugePanelEditor extends PureComponent<PanelEditorProps<GaugeOption
render() { render() {
const { options, fieldConfig } = this.props; const { options, fieldConfig } = this.props;
const { showThresholdLabels, showThresholdMarkers, fieldOptions } = options; const { showThresholdLabels, showThresholdMarkers, reduceOptions: valueOptions } = options;
const { defaults } = fieldConfig; const { defaults } = fieldConfig;
const suggestions = fieldOptions.values const suggestions = valueOptions.values
? getDataLinksVariableSuggestions(this.props.data.series) ? getDataLinksVariableSuggestions(this.props.data.series)
: getCalculationValueDataLinksVariableSuggestions(this.props.data.series); : getCalculationValueDataLinksVariableSuggestions(this.props.data.series);
return ( return (
<NewPanelEditorContext.Consumer> <NewPanelEditorContext.Consumer>
{useNewEditor => { {useNewEditor => {
if (useNewEditor) {
return null;
}
return ( return (
<> <>
<PanelOptionsGrid> <PanelOptionsGrid>
<PanelOptionsGroup title="Display"> <PanelOptionsGroup title="Display">
<FieldDisplayEditor <FieldDisplayEditor
onChange={this.onDisplayOptionsChanged} onChange={this.onDisplayOptionsChanged}
value={fieldOptions} value={valueOptions}
labelWidth={this.labelWidth} labelWidth={this.labelWidth}
/> />
<Switch <Switch
...@@ -128,36 +132,27 @@ export class GaugePanelEditor extends PureComponent<PanelEditorProps<GaugeOption ...@@ -128,36 +132,27 @@ export class GaugePanelEditor extends PureComponent<PanelEditorProps<GaugeOption
/> />
</PanelOptionsGroup> </PanelOptionsGroup>
<> <PanelOptionsGroup title="Field">
{!useNewEditor && ( <FieldPropertiesEditor
<> showMinMax={true}
<PanelOptionsGroup title="Field"> showTitle={true}
<FieldPropertiesEditor onChange={this.onDefaultsChange}
showMinMax={true} value={defaults}
showTitle={true} />
onChange={this.onDefaultsChange} </PanelOptionsGroup>
value={defaults}
/> <ThresholdsEditor onChange={this.onThresholdsChanged} thresholds={defaults.thresholds} />
</PanelOptionsGroup>
<ThresholdsEditor onChange={this.onThresholdsChanged} thresholds={defaults.thresholds} />
</>
)}
</>
</PanelOptionsGrid> </PanelOptionsGrid>
{!useNewEditor && (
<> <LegacyValueMappingsEditor onChange={this.onValueMappingsChanged} valueMappings={defaults.mappings} />
<LegacyValueMappingsEditor onChange={this.onValueMappingsChanged} valueMappings={defaults.mappings} /> <PanelOptionsGroup title="Data links">
<PanelOptionsGroup title="Data links"> <DataLinksEditor
<DataLinksEditor value={defaults.links}
value={defaults.links} onChange={this.onDataLinksChanged}
onChange={this.onDataLinksChanged} suggestions={suggestions}
suggestions={suggestions} maxLinks={10}
maxLinks={10} />
/> </PanelOptionsGroup>
</PanelOptionsGroup>
</>
)}
</> </>
); );
}} }}
......
...@@ -2,12 +2,14 @@ ...@@ -2,12 +2,14 @@
exports[`Gauge Panel Migrations from 6.1.1 1`] = ` exports[`Gauge Panel Migrations from 6.1.1 1`] = `
Object { Object {
"fieldOptions": Object { "orientation": "auto",
"reduceOptions": Object {
"calcs": Array [ "calcs": Array [
"last", "last",
], ],
"limit": undefined,
"values": undefined,
}, },
"orientation": "auto",
"showThresholdLabels": true, "showThresholdLabels": true,
"showThresholdMarkers": true, "showThresholdMarkers": true,
} }
......
...@@ -2,12 +2,27 @@ import { PanelPlugin } from '@grafana/data'; ...@@ -2,12 +2,27 @@ import { PanelPlugin } from '@grafana/data';
import { GaugePanelEditor } from './GaugePanelEditor'; import { GaugePanelEditor } from './GaugePanelEditor';
import { GaugePanel } from './GaugePanel'; import { GaugePanel } from './GaugePanel';
import { GaugeOptions, defaults } from './types'; import { GaugeOptions, defaults } from './types';
import { standardFieldConfig } from '../stat/types'; import { standardFieldConfig, 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) .setDefaults(defaults)
.setFieldConfigDefaults(standardFieldConfig) .setFieldConfigDefaults(standardFieldConfig)
.setEditor(GaugePanelEditor) .setEditor(GaugePanelEditor)
.setPanelOptions(builder => {
addStandardDataReduceOptions(builder);
builder
.addBooleanSwitch({
id: 'showThresholdLabels',
name: 'Show threshold Labels',
description: 'Render the threshold values around the gauge bar',
})
.addBooleanSwitch({
id: 'showThresholdMarkers',
name: 'Show threshold markers',
description: 'Renders the thresholds as an outer bar',
});
})
.setPanelChangeHandler(gaugePanelChangedHandler) .setPanelChangeHandler(gaugePanelChangedHandler)
.setMigrationHandler(gaugePanelMigrationHandler); .setMigrationHandler(gaugePanelMigrationHandler);
import { VizOrientation, FieldDisplayOptions, 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 { standardFieldDisplayOptions } from '../stat/types'; import { commonValueOptionDefaults } from '../stat/types';
export interface GaugeOptions extends SingleStatBaseOptions { export interface GaugeOptions extends SingleStatBaseOptions {
showThresholdLabels: boolean; showThresholdLabels: boolean;
showThresholdMarkers: boolean; showThresholdMarkers: boolean;
} }
export const standardGaugeFieldOptions: FieldDisplayOptions = {
...standardFieldDisplayOptions,
};
export const orientationOptions: Array<SelectableValue<VizOrientation>> = [ export const orientationOptions: Array<SelectableValue<VizOrientation>> = [
{ value: VizOrientation.Auto, label: 'Auto' }, { value: VizOrientation.Auto, label: 'Auto' },
{ value: VizOrientation.Horizontal, label: 'Horizontal' }, { value: VizOrientation.Horizontal, label: 'Horizontal' },
...@@ -20,6 +16,6 @@ export const orientationOptions: Array<SelectableValue<VizOrientation>> = [ ...@@ -20,6 +16,6 @@ export const orientationOptions: Array<SelectableValue<VizOrientation>> = [
export const defaults: GaugeOptions = { export const defaults: GaugeOptions = {
showThresholdMarkers: true, showThresholdMarkers: true,
showThresholdLabels: false, showThresholdLabels: false,
fieldOptions: standardGaugeFieldOptions, reduceOptions: commonValueOptionDefaults,
orientation: VizOrientation.Auto, orientation: VizOrientation.Auto,
}; };
import { LegendOptions, GraphTooltipOptions } from '@grafana/ui'; import { LegendOptions, GraphTooltipOptions } from '@grafana/ui';
import { YAxis, FieldDisplayOptions } from '@grafana/data'; import { YAxis } from '@grafana/data';
import { GraphLegendEditorLegendOptions } from './GraphLegendEditor'; import { GraphLegendEditorLegendOptions } from './GraphLegendEditor';
// TODO move out from single stat
import { standardFieldDisplayOptions } from '../stat/types';
export interface SeriesOptions { export interface SeriesOptions {
color?: string; color?: string;
...@@ -22,7 +19,6 @@ export interface Options { ...@@ -22,7 +19,6 @@ export interface Options {
series: { series: {
[alias: string]: SeriesOptions; [alias: string]: SeriesOptions;
}; };
fieldOptions: FieldDisplayOptions;
tooltipOptions: GraphTooltipOptions; tooltipOptions: GraphTooltipOptions;
} }
...@@ -38,6 +34,5 @@ export const defaults: Options = { ...@@ -38,6 +34,5 @@ export const defaults: Options = {
placement: 'under', placement: 'under',
}, },
series: {}, series: {},
fieldOptions: { ...standardFieldDisplayOptions },
tooltipOptions: { mode: 'single' }, tooltipOptions: { mode: 'single' },
}; };
...@@ -20,7 +20,7 @@ export class PieChartPanel extends PureComponent<Props> { ...@@ -20,7 +20,7 @@ export class PieChartPanel extends PureComponent<Props> {
const values = getFieldDisplayValues({ const values = getFieldDisplayValues({
fieldConfig, fieldConfig,
fieldOptions: options.fieldOptions, reduceOptions: options.reduceOptions,
data: data.series, data: data.series,
theme: config.theme, theme: config.theme,
replaceVariables: replaceVariables, replaceVariables: replaceVariables,
......
...@@ -6,7 +6,7 @@ import { ...@@ -6,7 +6,7 @@ import {
FieldPropertiesEditor, FieldPropertiesEditor,
LegacyValueMappingsEditor, LegacyValueMappingsEditor,
} from '@grafana/ui'; } from '@grafana/ui';
import { PanelEditorProps, FieldDisplayOptions, ValueMapping, FieldConfig } from '@grafana/data'; import { PanelEditorProps, ReduceDataOptions, ValueMapping, FieldConfig } from '@grafana/data';
import { PieChartOptionsBox } from './PieChartOptionsBox'; import { PieChartOptionsBox } from './PieChartOptionsBox';
import { PieChartOptions } from './types'; import { PieChartOptions } from './types';
...@@ -23,10 +23,10 @@ export class PieChartPanelEditor extends PureComponent<PanelEditorProps<PieChart ...@@ -23,10 +23,10 @@ export class PieChartPanelEditor extends PureComponent<PanelEditorProps<PieChart
}); });
}; };
onDisplayOptionsChanged = (fieldOptions: FieldDisplayOptions) => onDisplayOptionsChanged = (fieldOptions: ReduceDataOptions) =>
this.props.onOptionsChange({ this.props.onOptionsChange({
...this.props.options, ...this.props.options,
fieldOptions, reduceOptions: fieldOptions,
}); });
onDefaultsChange = (field: FieldConfig) => { onDefaultsChange = (field: FieldConfig) => {
...@@ -38,7 +38,7 @@ export class PieChartPanelEditor extends PureComponent<PanelEditorProps<PieChart ...@@ -38,7 +38,7 @@ export class PieChartPanelEditor extends PureComponent<PanelEditorProps<PieChart
render() { render() {
const { onOptionsChange, options, data, fieldConfig, onFieldConfigChange } = this.props; const { onOptionsChange, options, data, fieldConfig, onFieldConfigChange } = this.props;
const { fieldOptions } = options; const { reduceOptions: fieldOptions } = options;
const { defaults } = fieldConfig; const { defaults } = fieldConfig;
return ( return (
......
import { PieChartType, SingleStatBaseOptions } from '@grafana/ui'; import { PieChartType, SingleStatBaseOptions } from '@grafana/ui';
import { standardFieldDisplayOptions } from '../stat/types'; import { commonValueOptionDefaults } from '../stat/types';
import { ReducerID, VizOrientation } from '@grafana/data'; import { VizOrientation } from '@grafana/data';
export interface PieChartOptions extends SingleStatBaseOptions { export interface PieChartOptions extends SingleStatBaseOptions {
pieType: PieChartType; pieType: PieChartType;
...@@ -11,8 +11,5 @@ export const defaults: PieChartOptions = { ...@@ -11,8 +11,5 @@ export const defaults: PieChartOptions = {
pieType: PieChartType.PIE, pieType: PieChartType.PIE,
strokeWidth: 1, strokeWidth: 1,
orientation: VizOrientation.Auto, orientation: VizOrientation.Auto,
fieldOptions: { reduceOptions: commonValueOptionDefaults,
...standardFieldDisplayOptions,
calcs: [ReducerID.last],
},
}; };
...@@ -42,7 +42,7 @@ export class StatPanel extends PureComponent<PanelProps<StatPanelOptions>> { ...@@ -42,7 +42,7 @@ export class StatPanel extends PureComponent<PanelProps<StatPanelOptions>> {
yMax: value.field.max, yMax: value.field.max,
}; };
const calc = options.fieldOptions.calcs[0]; const calc = options.reduceOptions.calcs[0];
if (calc === ReducerID.last) { if (calc === ReducerID.last) {
sparkline.highlightIndex = sparkline.data.length - 1; sparkline.highlightIndex = sparkline.data.length - 1;
} }
...@@ -76,7 +76,7 @@ export class StatPanel extends PureComponent<PanelProps<StatPanelOptions>> { ...@@ -76,7 +76,7 @@ export class StatPanel extends PureComponent<PanelProps<StatPanelOptions>> {
return getFieldDisplayValues({ return getFieldDisplayValues({
fieldConfig, fieldConfig,
fieldOptions: options.fieldOptions, reduceOptions: options.reduceOptions,
replaceVariables, replaceVariables,
theme: config.theme, theme: config.theme,
data: data.series, data: data.series,
......
...@@ -15,7 +15,7 @@ import { ...@@ -15,7 +15,7 @@ import {
import { import {
PanelEditorProps, PanelEditorProps,
FieldDisplayOptions, ReduceDataOptions,
FieldConfig, FieldConfig,
ValueMapping, ValueMapping,
ThresholdsConfig, ThresholdsConfig,
...@@ -53,10 +53,10 @@ export class StatPanelEditor extends PureComponent<PanelEditorProps<StatPanelOpt ...@@ -53,10 +53,10 @@ export class StatPanelEditor extends PureComponent<PanelEditorProps<StatPanelOpt
}); });
}; };
onDisplayOptionsChanged = (fieldOptions: FieldDisplayOptions) => onDisplayOptionsChanged = (fieldOptions: ReduceDataOptions) =>
this.props.onOptionsChange({ this.props.onOptionsChange({
...this.props.options, ...this.props.options,
fieldOptions, reduceOptions: fieldOptions,
}); });
onColorModeChanged = ({ value }: any) => this.props.onOptionsChange({ ...this.props.options, colorMode: value }); onColorModeChanged = ({ value }: any) => this.props.onOptionsChange({ ...this.props.options, colorMode: value });
...@@ -84,21 +84,25 @@ export class StatPanelEditor extends PureComponent<PanelEditorProps<StatPanelOpt ...@@ -84,21 +84,25 @@ export class StatPanelEditor extends PureComponent<PanelEditorProps<StatPanelOpt
render() { render() {
const { options, fieldConfig } = this.props; const { options, fieldConfig } = this.props;
const { fieldOptions } = options; const { reduceOptions: valueOptions } = options;
const { defaults } = fieldConfig; const { defaults } = fieldConfig;
const suggestions = fieldOptions.values const suggestions = valueOptions.values
? getDataLinksVariableSuggestions(this.props.data.series) ? getDataLinksVariableSuggestions(this.props.data.series)
: getCalculationValueDataLinksVariableSuggestions(this.props.data.series); : getCalculationValueDataLinksVariableSuggestions(this.props.data.series);
return ( return (
<NewPanelEditorContext.Consumer> <NewPanelEditorContext.Consumer>
{useNewEditor => { {useNewEditor => {
if (useNewEditor) {
return null;
}
return ( return (
<> <>
<PanelOptionsGrid> <PanelOptionsGrid>
<PanelOptionsGroup title="Display"> <PanelOptionsGroup title="Display">
<FieldDisplayEditor onChange={this.onDisplayOptionsChanged} value={fieldOptions} labelWidth={8} /> <FieldDisplayEditor onChange={this.onDisplayOptionsChanged} value={valueOptions} labelWidth={8} />
<div className="form-field"> <div className="form-field">
<FormLabel width={8}>Orientation</FormLabel> <FormLabel width={8}>Orientation</FormLabel>
<Select <Select
...@@ -140,36 +144,28 @@ export class StatPanelEditor extends PureComponent<PanelEditorProps<StatPanelOpt ...@@ -140,36 +144,28 @@ export class StatPanelEditor extends PureComponent<PanelEditorProps<StatPanelOpt
/> />
</div> </div>
</PanelOptionsGroup> </PanelOptionsGroup>
<>
{!useNewEditor && ( <PanelOptionsGroup title="Field">
<> <FieldPropertiesEditor
<PanelOptionsGroup title="Field"> showMinMax={true}
<FieldPropertiesEditor onChange={this.onDefaultsChange}
showMinMax={true} value={defaults}
onChange={this.onDefaultsChange} showTitle={true}
value={defaults} />
showTitle={true} </PanelOptionsGroup>
/> <ThresholdsEditor onChange={this.onThresholdsChanged} thresholds={defaults.thresholds} />
</PanelOptionsGroup>
<ThresholdsEditor onChange={this.onThresholdsChanged} thresholds={defaults.thresholds} />
</>
)}
</>
</PanelOptionsGrid> </PanelOptionsGrid>
{!useNewEditor && (
<> <LegacyValueMappingsEditor onChange={this.onValueMappingsChanged} valueMappings={defaults.mappings} />
<LegacyValueMappingsEditor onChange={this.onValueMappingsChanged} valueMappings={defaults.mappings} />
<PanelOptionsGroup title="Data links">
<PanelOptionsGroup title="Data links"> <DataLinksEditor
<DataLinksEditor value={defaults.links}
value={defaults.links} onChange={this.onDataLinksChanged}
onChange={this.onDataLinksChanged} suggestions={suggestions}
suggestions={suggestions} maxLinks={10}
maxLinks={10} />
/> </PanelOptionsGroup>
</PanelOptionsGroup>
</>
)}
</> </>
); );
}} }}
......
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, standardFieldConfig } from './types'; import { StatPanelOptions, defaults, standardFieldConfig, addStandardDataReduceOptions } from './types';
import { StatPanel } from './StatPanel'; import { StatPanel } from './StatPanel';
import { StatPanelEditor } from './StatPanelEditor'; import { StatPanelEditor } from './StatPanelEditor';
...@@ -8,6 +8,56 @@ export const plugin = new PanelPlugin<StatPanelOptions>(StatPanel) ...@@ -8,6 +8,56 @@ export const plugin = new PanelPlugin<StatPanelOptions>(StatPanel)
.setDefaults(defaults) .setDefaults(defaults)
.setFieldConfigDefaults(standardFieldConfig) .setFieldConfigDefaults(standardFieldConfig)
.setEditor(StatPanelEditor) .setEditor(StatPanelEditor)
.setPanelOptions(builder => {
addStandardDataReduceOptions(builder);
builder
.addRadio({
id: 'orientation',
name: 'Orientation',
description: 'Stacking direction for multiple bars',
settings: {
options: [
{ value: 'auto', label: 'Auto' },
{ value: 'horizontal', label: 'Horizontal' },
{ value: 'vertical', label: 'Vertical' },
],
},
})
.addRadio({
id: 'colorMode',
name: 'Color mode',
description: 'Color either the value or the background',
settings: {
options: [
{ value: 'value', label: 'Value' },
{ value: 'background', label: 'Background' },
],
},
})
.addRadio({
id: 'graphMode',
name: 'Graph mode',
description: 'Stat panel graph / sparkline mode',
settings: {
options: [
{ value: 'none', label: 'None' },
{ value: 'area', label: 'Area' },
],
},
})
.addRadio({
id: 'justifyMode',
name: 'Justify mode',
description: 'Value & title posititioning',
settings: {
options: [
{ value: 'auto', label: 'Auto' },
{ value: 'center', label: 'Center' },
],
},
});
})
.setNoPadding() .setNoPadding()
.setPanelChangeHandler(sharedSingleStatPanelChangedHandler) .setPanelChangeHandler(sharedSingleStatPanelChangedHandler)
.setMigrationHandler(sharedSingleStatMigrationHandler); .setMigrationHandler(sharedSingleStatMigrationHandler);
...@@ -2,10 +2,11 @@ import { SingleStatBaseOptions, BigValueColorMode, BigValueGraphMode, BigValueJu ...@@ -2,10 +2,11 @@ import { SingleStatBaseOptions, BigValueColorMode, BigValueGraphMode, BigValueJu
import { import {
VizOrientation, VizOrientation,
ReducerID, ReducerID,
FieldDisplayOptions, ReduceDataOptions,
SelectableValue, SelectableValue,
FieldConfigSource, FieldConfigSource,
ThresholdsMode, ThresholdsMode,
standardEditorsRegistry,
} from '@grafana/data'; } from '@grafana/data';
import { PanelOptionsEditorBuilder } from '@grafana/data/src/utils/OptionsUIBuilders'; import { PanelOptionsEditorBuilder } from '@grafana/data/src/utils/OptionsUIBuilders';
...@@ -31,7 +32,7 @@ export const justifyModes: Array<SelectableValue<BigValueJustifyMode>> = [ ...@@ -31,7 +32,7 @@ export const justifyModes: Array<SelectableValue<BigValueJustifyMode>> = [
{ value: BigValueJustifyMode.Center, label: 'Center' }, { value: BigValueJustifyMode.Center, label: 'Center' },
]; ];
export const standardFieldDisplayOptions: FieldDisplayOptions = { export const commonValueOptionDefaults: ReduceDataOptions = {
values: false, values: false,
calcs: [ReducerID.mean], calcs: [ReducerID.mean],
}; };
...@@ -50,9 +51,9 @@ export const standardFieldConfig: FieldConfigSource = { ...@@ -50,9 +51,9 @@ export const standardFieldConfig: FieldConfigSource = {
overrides: [], overrides: [],
}; };
export function addStandardSingleValueOptions(builder: PanelOptionsEditorBuilder) { export function addStandardDataReduceOptions(builder: PanelOptionsEditorBuilder) {
builder.addRadio({ builder.addRadio({
id: 'values', id: 'reduceOptions.values',
name: 'Show', name: 'Show',
description: 'Calculate a single value per colum or series or show each row', description: 'Calculate a single value per colum or series or show each row',
settings: { settings: {
...@@ -62,12 +63,44 @@ export function addStandardSingleValueOptions(builder: PanelOptionsEditorBuilder ...@@ -62,12 +63,44 @@ export function addStandardSingleValueOptions(builder: PanelOptionsEditorBuilder
], ],
}, },
}); });
builder.addNumberInput({
id: 'reduceOptions.limit',
name: 'Limit',
description: 'Max number of rows to display',
settings: {
placeholder: '5000',
integer: true,
min: 1,
max: 5000,
},
});
builder.addCustomEditor({
id: 'reduceOptions.calcs',
name: 'Value',
description: 'Choose a reducer function / calculation',
editor: standardEditorsRegistry.get('stats-picker').editor as any,
});
builder.addRadio({
id: 'orientation',
name: 'Orientation',
description: 'Stacking direction in case of multiple series or fields',
settings: {
options: [
{ value: 'auto', label: 'Auto' },
{ value: 'horizontal', label: 'Horizontal' },
{ value: 'vertical', label: 'Vertical' },
],
},
});
} }
export const defaults: StatPanelOptions = { export const defaults: StatPanelOptions = {
graphMode: BigValueGraphMode.Area, graphMode: BigValueGraphMode.Area,
colorMode: BigValueColorMode.Value, colorMode: BigValueColorMode.Value,
justifyMode: BigValueJustifyMode.Auto, justifyMode: BigValueJustifyMode.Auto,
fieldOptions: standardFieldDisplayOptions, reduceOptions: commonValueOptionDefaults,
orientation: VizOrientation.Auto, orientation: VizOrientation.Auto,
}; };
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