BarGauge: First bar gauge panel option & some refactoring (#23128)

* First bar gauge panel option

* Update doc comments

* Minor changes

* progress

* Fixing typing errors

* Minor type updates

* Fix that TS!

* Bring satisfaction to that beast called typescript

Co-authored-by: Dominik Prokop <dominik.prokop@grafana.com>
This commit is contained in:
Torkel Ödegaard
2020-03-27 12:38:46 +01:00
committed by GitHub
parent 7e094ac559
commit 08133ea31a
19 changed files with 150 additions and 117 deletions

View File

@ -15,10 +15,10 @@ export interface NumberFieldConfigSettings {
export const numberOverrideProcessor = ( export const numberOverrideProcessor = (
value: any, value: any,
context: FieldOverrideContext, context: FieldOverrideContext,
settings: NumberFieldConfigSettings settings?: NumberFieldConfigSettings
) => { ) => {
const v = parseFloat(`${value}`); const v = parseFloat(`${value}`);
if (settings.max && v > settings.max) { if (settings && settings.max && v > settings.max) {
// ???? // ????
} }
return v; return v;
@ -29,7 +29,7 @@ export interface DataLinksFieldConfigSettings {}
export const dataLinksOverrideProcessor = ( export const dataLinksOverrideProcessor = (
value: any, value: any,
_context: FieldOverrideContext, _context: FieldOverrideContext,
_settings: DataLinksFieldConfigSettings _settings?: DataLinksFieldConfigSettings
) => { ) => {
return value as DataLink[]; return value as DataLink[];
}; };
@ -39,7 +39,7 @@ export interface ValueMappingFieldConfigSettings {}
export const valueMappingsOverrideProcessor = ( export const valueMappingsOverrideProcessor = (
value: any, value: any,
_context: FieldOverrideContext, _context: FieldOverrideContext,
_settings: ValueMappingFieldConfigSettings _settings?: ValueMappingFieldConfigSettings
) => { ) => {
return value as ValueMapping[]; // !!!! likely not !!!! return value as ValueMapping[]; // !!!! likely not !!!!
}; };
@ -51,7 +51,7 @@ export interface SelectFieldConfigSettings<T> {
export const selectOverrideProcessor = ( export const selectOverrideProcessor = (
value: any, value: any,
_context: FieldOverrideContext, _context: FieldOverrideContext,
_settings: SelectFieldConfigSettings<any> _settings?: SelectFieldConfigSettings<any>
) => { ) => {
return value; return value;
}; };
@ -65,9 +65,9 @@ export interface StringFieldConfigSettings {
export const stringOverrideProcessor = ( export const stringOverrideProcessor = (
value: any, value: any,
context: FieldOverrideContext, context: FieldOverrideContext,
settings: StringFieldConfigSettings settings?: StringFieldConfigSettings
) => { ) => {
if (settings.expandTemplateVars && context.replaceVariables) { if (settings && settings.expandTemplateVars && context.replaceVariables) {
return context.replaceVariables(value, context.field!.config.scopedVars); return context.replaceVariables(value, context.field!.config.scopedVars);
} }
return `${value}`; return `${value}`;
@ -80,7 +80,7 @@ export interface ThresholdsFieldConfigSettings {
export const thresholdsOverrideProcessor = ( export const thresholdsOverrideProcessor = (
value: any, value: any,
_context: FieldOverrideContext, _context: FieldOverrideContext,
_settings: ThresholdsFieldConfigSettings _settings?: ThresholdsFieldConfigSettings
) => { ) => {
return value as ThresholdsConfig; // !!!! likely not !!!! return value as ThresholdsConfig; // !!!! likely not !!!!
}; };
@ -90,7 +90,7 @@ export interface UnitFieldConfigSettings {}
export const unitOverrideProcessor = ( export const unitOverrideProcessor = (
value: boolean, value: boolean,
_context: FieldOverrideContext, _context: FieldOverrideContext,
_settings: UnitFieldConfigSettings _settings?: UnitFieldConfigSettings
) => { ) => {
return value; return value;
}; };
@ -98,7 +98,7 @@ export const unitOverrideProcessor = (
export const booleanOverrideProcessor = ( export const booleanOverrideProcessor = (
value: boolean, value: boolean,
_context: FieldOverrideContext, _context: FieldOverrideContext,
_settings: ThresholdsFieldConfigSettings _settings?: ThresholdsFieldConfigSettings
) => { ) => {
return value; // !!!! likely not !!!! return value; // !!!! likely not !!!!
}; };

View File

@ -9,7 +9,7 @@ describe('PanelPlugin', () => {
return <div>Panel</div>; return <div>Panel</div>;
}); });
panel.setCustomFieldConfigEditor(builder => { panel.setCustomFieldOptions(builder => {
builder.addCustomEditor({ builder.addCustomEditor({
id: 'custom', id: 'custom',
name: 'Custom', name: 'Custom',
@ -31,7 +31,7 @@ describe('PanelPlugin', () => {
return <div>Panel</div>; return <div>Panel</div>;
}); });
panel.setOptionsEditor(builder => { panel.setPanelOptions(builder => {
builder.addCustomEditor({ builder.addCustomEditor({
id: 'option', id: 'option',
name: 'Option editor', name: 'Option editor',

View File

@ -110,7 +110,7 @@ export class PanelPlugin<TOptions = any> extends GrafanaPlugin<PanelPluginMeta>
* interface ShapePanelOptions {} * interface ShapePanelOptions {}
* *
* export const plugin = new PanelPlugin<ShapePanelOptions>(ShapePanel) * export const plugin = new PanelPlugin<ShapePanelOptions>(ShapePanel)
* .setCustomFieldConfigEditor(builder => { * .setCustomFieldOptions(builder => {
* builder * builder
* .addNumberInput({ * .addNumberInput({
* id: 'shapeBorderWidth', * id: 'shapeBorderWidth',
@ -134,7 +134,7 @@ export class PanelPlugin<TOptions = any> extends GrafanaPlugin<PanelPluginMeta>
* *
* @public * @public
**/ **/
setCustomFieldConfigEditor(builder: (builder: FieldConfigEditorBuilder) => void) { setCustomFieldOptions(builder: (builder: FieldConfigEditorBuilder) => void) {
// builder is applied lazily when custom field configs are accessed // builder is applied lazily when custom field configs are accessed
this.registerCustomFieldConfigs = builder; this.registerCustomFieldConfigs = builder;
return this; return this;
@ -151,7 +151,7 @@ export class PanelPlugin<TOptions = any> extends GrafanaPlugin<PanelPluginMeta>
* interface ShapePanelOptions {} * interface ShapePanelOptions {}
* *
* export const plugin = new PanelPlugin<ShapePanelOptions>(ShapePanel) * export const plugin = new PanelPlugin<ShapePanelOptions>(ShapePanel)
* .setOptionsEditor(builder => { * .setPanelOptions(builder => {
* builder * builder
* .addSelect({ * .addSelect({
* id: 'shape', * id: 'shape',
@ -170,7 +170,7 @@ export class PanelPlugin<TOptions = any> extends GrafanaPlugin<PanelPluginMeta>
* *
* @public * @public
**/ **/
setOptionsEditor(builder: (builder: PanelOptionsEditorBuilder) => void) { setPanelOptions(builder: (builder: PanelOptionsEditorBuilder) => void) {
// builder is applied lazily when options UI is created // builder is applied lazily when options UI is created
this.registerOptionEditors = builder; this.registerOptionEditors = builder;
return this; return this;

View File

@ -5,44 +5,46 @@ import { NumberFieldConfigSettings, SelectFieldConfigSettings, StringFieldConfig
/** /**
* Option editor registry item * Option editor registry item
*/ */
interface OptionsEditorItem<TSettings, TEditorProps> extends RegistryItem { export interface OptionsEditorItem<TSettings, TEditorProps> extends RegistryItem {
editor: ComponentType<TEditorProps>;
settings?: TSettings; settings?: TSettings;
editor?: ComponentType<TEditorProps>;
} }
/** /**
* Configuration of option editor registry item * Configuration of option editor registry item
*/ */
type OptionEditorConfig<TSettings, TEditorProps> = Pick< interface OptionEditorConfig<TSettings> {
OptionsEditorItem<TSettings, TEditorProps>, id: string;
'id' | 'name' | 'description' | 'editor' | 'settings' name: string;
>; description: string;
settings?: TSettings;
}
/** /**
* Describes an API for option editors UI builder * Describes an API for option editors UI builder
*/ */
export interface OptionsUIRegistryBuilderAPI<TEditorProps, T extends OptionsEditorItem<any, TEditorProps>> { export interface OptionsUIRegistryBuilderAPI<TEditorProps, T extends OptionsEditorItem<any, TEditorProps>> {
addNumberInput?<TSettings extends NumberFieldConfigSettings = NumberFieldConfigSettings>( addNumberInput?<TSettings extends NumberFieldConfigSettings = NumberFieldConfigSettings>(
config: OptionEditorConfig<TSettings, TEditorProps> config: OptionEditorConfig<TSettings>
): this; ): this;
addTextInput?<TSettings extends StringFieldConfigSettings = StringFieldConfigSettings>( addTextInput?<TSettings extends StringFieldConfigSettings = StringFieldConfigSettings>(
config: OptionEditorConfig<TSettings, TEditorProps> config: OptionEditorConfig<TSettings>
): this; ): this;
addSelect?<TOption, TSettings extends SelectFieldConfigSettings<TOption>>( addSelect?<TOption, TSettings extends SelectFieldConfigSettings<TOption>>(
config: OptionEditorConfig<TSettings, TEditorProps> config: OptionEditorConfig<TSettings>
): this; ): this;
addRadio?<TOption, TSettings extends SelectFieldConfigSettings<TOption> = SelectFieldConfigSettings<TOption>>( addRadio?<TOption, TSettings extends SelectFieldConfigSettings<TOption> = SelectFieldConfigSettings<TOption>>(
config: OptionEditorConfig<TSettings, TEditorProps> config: OptionEditorConfig<TSettings>
): this; ): this;
addBooleanSwitch?<TSettings = any>(config: OptionEditorConfig<TSettings, TEditorProps>): this; addBooleanSwitch?<TSettings = any>(config: OptionEditorConfig<TSettings>): this;
addUnitPicker?<TSettings = any>(config: OptionEditorConfig<TSettings, TEditorProps>): this; addUnitPicker?<TSettings = any>(config: OptionEditorConfig<TSettings>): this;
addColorPicker?<TSettings = any>(config: OptionEditorConfig<TSettings, TEditorProps>): this; addColorPicker?<TSettings = any>(config: OptionEditorConfig<TSettings>): this;
/** /**
* Enables custom editor definition * Enables custom editor definition

View File

@ -9,9 +9,10 @@ import {
GrafanaTheme, GrafanaTheme,
TimeZone, TimeZone,
} from '../types'; } from '../types';
import { Registry, RegistryItem } from '../utils'; import { Registry } from '../utils';
import { InterpolateFunction } from './panel'; import { InterpolateFunction } from './panel';
import { StandardEditorProps } from '../field'; import { StandardEditorProps } from '../field';
import { OptionsEditorItem } from './OptionsUIRegistryBuilder';
export interface DynamicConfigValue { export interface DynamicConfigValue {
prop: string; prop: string;
@ -53,24 +54,21 @@ export interface FieldOverrideEditorProps<TValue, TSettings> extends Omit<Standa
context: FieldOverrideContext; context: FieldOverrideContext;
} }
export interface FieldConfigEditorConfig<TSettings = any, TValue = any> export interface FieldConfigEditorConfig<TSettings = any> {
extends Omit<Pick<FieldPropertyEditorItem<TValue, TSettings>, 'id' | 'description' | 'name'>, 'settings'> { id: string;
name: string;
description: string;
settings?: TSettings; settings?: TSettings;
shouldApply?: (field: Field) => boolean; shouldApply?: (field: Field) => boolean;
} }
export interface FieldPropertyEditorItem<TValue = any, TSettings = any> extends RegistryItem { export interface FieldPropertyEditorItem<TValue = any, TSettings extends {} = any>
// An editor the creates the well typed value extends OptionsEditorItem<TSettings, FieldConfigEditorProps<TValue, TSettings>> {
editor: ComponentType<FieldConfigEditorProps<TValue, TSettings>>;
// An editor that can be filled in with context info (template variables etc) // An editor that can be filled in with context info (template variables etc)
override: ComponentType<FieldOverrideEditorProps<TValue, TSettings>>; override: ComponentType<FieldOverrideEditorProps<TValue, TSettings>>;
// Convert the override value to a well typed value // Convert the override value to a well typed value
process: (value: any, context: FieldOverrideContext, settings: TSettings) => TValue; process: (value: any, context: FieldOverrideContext, settings?: TSettings) => TValue;
// Configuration options for the particular property
settings: TSettings;
// Checks if field should be processed // Checks if field should be processed
shouldApply: (field: Field) => boolean; shouldApply: (field: Field) => boolean;

View File

@ -1,4 +1,3 @@
import { ComponentType } from 'react';
import { DataQueryError, DataQueryRequest, DataQueryTimings } from './datasource'; import { DataQueryError, DataQueryRequest, DataQueryTimings } from './datasource';
import { PluginMeta } from './plugin'; import { PluginMeta } from './plugin';
import { ScopedVars } from './ScopedVars'; import { ScopedVars } from './ScopedVars';
@ -6,8 +5,9 @@ import { LoadingState } from './data';
import { DataFrame } from './dataFrame'; import { DataFrame } from './dataFrame';
import { AbsoluteTimeRange, TimeRange, TimeZone } from './time'; import { AbsoluteTimeRange, TimeRange, TimeZone } from './time';
import { FieldConfigSource } from './fieldOverrides'; import { FieldConfigSource } from './fieldOverrides';
import { Registry, RegistryItem } from '../utils'; import { Registry } from '../utils';
import { StandardEditorProps } from '../field'; import { StandardEditorProps } from '../field';
import { OptionsEditorItem } from './OptionsUIRegistryBuilder';
export type InterpolateFunction = (value: string, scopedVars?: ScopedVars, format?: string | Function) => string; export type InterpolateFunction = (value: string, scopedVars?: ScopedVars, format?: string | Function) => string;
@ -115,14 +115,16 @@ export type PanelOptionEditorsRegistry = Registry<PanelOptionsEditorItem>;
export interface PanelOptionsEditorProps<TValue> extends StandardEditorProps<TValue> {} export interface PanelOptionsEditorProps<TValue> extends StandardEditorProps<TValue> {}
export interface PanelOptionsEditorItem<TValue = any, TSettings = any> extends RegistryItem { export interface PanelOptionsEditorItem<TValue = any, TSettings = any>
editor: ComponentType<PanelOptionsEditorProps<TValue>>; extends OptionsEditorItem<TSettings, PanelOptionsEditorProps<TValue>> {}
export interface PanelOptionsEditorConfig<TSettings = any> {
id: string;
name: string;
description: string;
settings?: TSettings; settings?: TSettings;
} }
export interface PanelOptionsEditorConfig<TSettings = any, TValue = any>
extends Pick<PanelOptionsEditorItem<TValue, TSettings>, 'id' | 'description' | 'name' | 'settings'> {}
export interface PanelMenuItem { export interface PanelMenuItem {
type?: 'submenu' | 'divider'; type?: 'submenu' | 'divider';
text?: string; text?: string;

View File

@ -16,14 +16,14 @@ export const NumberValueEditor: React.FC<FieldConfigEditorProps<number, NumberFi
return ( return (
<Forms.Input <Forms.Input
value={isNaN(value) ? '' : value} value={isNaN(value) ? '' : value}
min={settings.min} min={settings?.min}
max={settings.max} max={settings?.max}
type="number" type="number"
step={settings.step} step={settings?.step}
placeholder={settings.placeholder} placeholder={settings?.placeholder}
onChange={e => { onChange={e => {
onChange( onChange(
settings.integer ? toIntegerOrUndefined(e.currentTarget.value) : toFloatOrUndefined(e.currentTarget.value) settings?.integer ? toIntegerOrUndefined(e.currentTarget.value) : toFloatOrUndefined(e.currentTarget.value)
); );
}} }}
/> />

View File

@ -7,5 +7,5 @@ export function SelectValueEditor<T>({
onChange, onChange,
item, item,
}: FieldConfigEditorProps<T, SelectFieldConfigSettings<T>>) { }: FieldConfigEditorProps<T, SelectFieldConfigSettings<T>>) {
return <Forms.Select<T> defaultValue={value} onChange={e => onChange(e.value)} options={item.settings.options} />; return <Forms.Select<T> defaultValue={value} onChange={e => onChange(e.value)} options={item.settings?.options} />;
} }

View File

@ -13,7 +13,7 @@ interface Props<V, D> {
/** /**
* Render a single value * Render a single value
*/ */
renderValue: (value: V, width: number, height: number, dims: D) => JSX.Element; renderValue: (props: VizRepeaterRenderValueProps<V, D>) => JSX.Element;
height: number; height: number;
width: number; width: number;
source: any; // If this changes, new values will be requested source: any; // If this changes, new values will be requested
@ -23,6 +23,14 @@ interface Props<V, D> {
itemSpacing?: number; itemSpacing?: number;
} }
export interface VizRepeaterRenderValueProps<V, D = {}> {
value: V;
width: number;
height: number;
orientation: VizOrientation;
alignmentFactors: D;
}
interface DefaultProps { interface DefaultProps {
itemSpacing: number; itemSpacing: number;
} }
@ -99,13 +107,14 @@ export class VizRepeater<V, D = {}> extends PureComponent<Props<V, D>, State<V>>
itemStyles.width = `${vizWidth}px`; itemStyles.width = `${vizWidth}px`;
itemStyles.height = `${vizHeight}px`; itemStyles.height = `${vizHeight}px`;
const dims = getAlignmentFactors ? getAlignmentFactors(values, vizWidth, vizHeight) : ({} as D); const alignmentFactors = getAlignmentFactors ? getAlignmentFactors(values, vizWidth, vizHeight) : ({} as D);
return ( return (
<div style={repeaterStyle}> <div style={repeaterStyle}>
{values.map((value, index) => { {values.map((value, index) => {
return ( return (
<div key={index} style={getItemStylesForIndex(itemStyles, index, values.length)}> <div key={index} style={getItemStylesForIndex(itemStyles, index, values.length)}>
{renderValue(value, vizWidth, vizHeight, dims)} {renderValue({ value, width: vizWidth, height: vizHeight, alignmentFactors, orientation })}
</div> </div>
); );
})} })}

View File

@ -78,7 +78,7 @@ export { GraphWithLegend } from './Graph/GraphWithLegend';
export { GraphContextMenu } from './Graph/GraphContextMenu'; export { GraphContextMenu } from './Graph/GraphContextMenu';
export { BarGauge, BarGaugeDisplayMode } from './BarGauge/BarGauge'; export { BarGauge, BarGaugeDisplayMode } from './BarGauge/BarGauge';
export { GraphTooltipOptions } from './Graph/GraphTooltip/types'; export { GraphTooltipOptions } from './Graph/GraphTooltip/types';
export { VizRepeater } from './VizRepeater/VizRepeater'; export { VizRepeater, VizRepeaterRenderValueProps } from './VizRepeater/VizRepeater';
export { export {
LegendOptions, LegendOptions,

View File

@ -315,6 +315,7 @@ export const PanelEditor = connect(mapStateToProps, mapDispatchToProps)(PanelEdi
*/ */
const getStyles = stylesFactory((theme: GrafanaTheme) => { const getStyles = stylesFactory((theme: GrafanaTheme) => {
const handleColor = theme.colors.blueLight; const handleColor = theme.colors.blueLight;
const paneSpaceing = theme.spacing.md;
const resizer = css` const resizer = css`
font-style: italic; font-style: italic;
@ -354,21 +355,21 @@ const getStyles = stylesFactory((theme: GrafanaTheme) => {
`, `,
panelWrapper: css` panelWrapper: css`
width: 100%; width: 100%;
padding-left: ${theme.spacing.sm}; padding-left: ${paneSpaceing};
height: 100%; height: 100%;
`, `,
resizerV: cx( resizerV: cx(
resizer, resizer,
css` css`
cursor: col-resize; cursor: col-resize;
width: 8px; width: ${paneSpaceing};
border-right-width: 1px; border-right-width: 1px;
` `
), ),
resizerH: cx( resizerH: cx(
resizer, resizer,
css` css`
height: 8px; height: ${paneSpaceing};
cursor: row-resize; cursor: row-resize;
position: relative; position: relative;
top: 49px; top: 49px;

View File

@ -4,7 +4,7 @@ import React, { PureComponent } from 'react';
// Services & Utils // Services & Utils
import { config } from 'app/core/config'; import { config } from 'app/core/config';
import { BarGauge, VizRepeater, DataLinksContextMenu } from '@grafana/ui'; import { BarGauge, VizRepeater, VizRepeaterRenderValueProps, DataLinksContextMenu } from '@grafana/ui';
import { BarGaugeOptions } from './types'; import { BarGaugeOptions } from './types';
import { import {
getFieldDisplayValues, getFieldDisplayValues,
@ -16,13 +16,9 @@ import {
import { getFieldLinksSupplier } from 'app/features/panel/panellinks/linkSuppliers'; import { getFieldLinksSupplier } from 'app/features/panel/panellinks/linkSuppliers';
export class BarGaugePanel extends PureComponent<PanelProps<BarGaugeOptions>> { export class BarGaugePanel extends PureComponent<PanelProps<BarGaugeOptions>> {
renderValue = ( renderValue = (valueProps: VizRepeaterRenderValueProps<FieldDisplay, DisplayValueAlignmentFactors>): JSX.Element => {
value: FieldDisplay,
width: number,
height: number,
alignmentFactors: DisplayValueAlignmentFactors
): JSX.Element => {
const { options } = this.props; const { options } = this.props;
const { value, alignmentFactors, orientation, width, height } = valueProps;
const { field, display, view, colIndex } = value; const { field, display, view, colIndex } = value;
return ( return (
@ -33,7 +29,7 @@ export class BarGaugePanel extends PureComponent<PanelProps<BarGaugeOptions>> {
value={display} value={display}
width={width} width={width}
height={height} height={height}
orientation={options.orientation} orientation={orientation}
field={field} field={field}
display={view?.getFieldDisplayProcessor(colIndex)} display={view?.getFieldDisplayProcessor(colIndex)}
theme={config.theme} theme={config.theme}

View File

@ -95,6 +95,10 @@ export class BarGaugePanelEditor extends PureComponent<PanelEditorProps<BarGauge
return ( return (
<NewPanelEditorContext.Consumer> <NewPanelEditorContext.Consumer>
{useNewEditor => { {useNewEditor => {
if (useNewEditor) {
return null;
}
return ( return (
<> <>
<PanelOptionsGrid> <PanelOptionsGrid>
@ -135,38 +139,28 @@ export class BarGaugePanelEditor extends PureComponent<PanelEditorProps<BarGauge
)} )}
</> </>
</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}
value={defaults}
/>
</PanelOptionsGroup>
<ThresholdsEditor onChange={this.onThresholdsChanged} thresholds={defaults.thresholds} />
</>
)}
</>
</PanelOptionsGrid>
{!useNewEditor && (
<LegacyValueMappingsEditor onChange={this.onValueMappingsChanged} valueMappings={defaults.mappings} />
)}
{!useNewEditor && (
<PanelOptionsGroup title="Data links">
<DataLinksEditor
value={defaults.links}
onChange={this.onDataLinksChanged}
suggestions={suggestions}
maxLinks={10}
/> />
</PanelOptionsGroup> </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>
</> </>
); );
}} }}

View File

@ -1,14 +1,30 @@
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 { BarGaugePanelEditor } from './BarGaugePanelEditor';
import { BarGaugeOptions, defaults } from './types'; import { BarGaugeOptions, defaults } from './types';
import { standardFieldConfig } from '../stat/types'; import { standardFieldConfig } 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) .setDefaults(defaults)
.setFieldConfigDefaults(standardFieldConfig)
.setEditor(BarGaugePanelEditor) .setEditor(BarGaugePanelEditor)
.setFieldConfigDefaults(standardFieldConfig)
.setPanelOptions(builder => {
/* addStandardSingleValueOptions(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' },
],
},
});
})
.setPanelChangeHandler(sharedSingleStatPanelChangedHandler) .setPanelChangeHandler(sharedSingleStatPanelChangedHandler)
.setMigrationHandler(barGaugePanelMigrationHandler); .setMigrationHandler(barGaugePanelMigrationHandler);

View File

@ -5,17 +5,17 @@ import React, { PureComponent } from 'react';
import { config } from 'app/core/config'; import { config } from 'app/core/config';
// Components // Components
import { Gauge, DataLinksContextMenu } from '@grafana/ui'; import { Gauge, DataLinksContextMenu, VizRepeater, VizRepeaterRenderValueProps } from '@grafana/ui';
// Types // Types
import { GaugeOptions } from './types'; import { GaugeOptions } from './types';
import { VizRepeater } from '@grafana/ui';
import { FieldDisplay, getFieldDisplayValues, VizOrientation, PanelProps } from '@grafana/data'; import { FieldDisplay, getFieldDisplayValues, VizOrientation, PanelProps } from '@grafana/data';
import { getFieldLinksSupplier } from 'app/features/panel/panellinks/linkSuppliers'; import { getFieldLinksSupplier } from 'app/features/panel/panellinks/linkSuppliers';
export class GaugePanel extends PureComponent<PanelProps<GaugeOptions>> { export class GaugePanel extends PureComponent<PanelProps<GaugeOptions>> {
renderValue = (value: FieldDisplay, width: number, height: number): JSX.Element => { renderValue = (valueProps: VizRepeaterRenderValueProps<FieldDisplay>): JSX.Element => {
const { options } = this.props; const { options } = this.props;
const { value, width, height } = valueProps;
const { field, display } = value; const { field, display } = value;
return ( return (

View File

@ -6,7 +6,14 @@ import { config } from 'app/core/config';
// Types // Types
import { StatPanelOptions } from './types'; import { StatPanelOptions } from './types';
import { VizRepeater, BigValue, DataLinksContextMenu, BigValueSparkline, BigValueGraphMode } from '@grafana/ui'; import {
VizRepeater,
VizRepeaterRenderValueProps,
BigValue,
DataLinksContextMenu,
BigValueSparkline,
BigValueGraphMode,
} from '@grafana/ui';
import { import {
PanelProps, PanelProps,
@ -21,13 +28,9 @@ import {
import { getFieldLinksSupplier } from 'app/features/panel/panellinks/linkSuppliers'; import { getFieldLinksSupplier } from 'app/features/panel/panellinks/linkSuppliers';
export class StatPanel extends PureComponent<PanelProps<StatPanelOptions>> { export class StatPanel extends PureComponent<PanelProps<StatPanelOptions>> {
renderValue = ( renderValue = (valueProps: VizRepeaterRenderValueProps<FieldDisplay, DisplayValueAlignmentFactors>): JSX.Element => {
value: FieldDisplay,
width: number,
height: number,
alignmentFactors: DisplayValueAlignmentFactors
): JSX.Element => {
const { timeRange, options } = this.props; const { timeRange, options } = this.props;
const { value, alignmentFactors, width, height } = valueProps;
let sparkline: BigValueSparkline | undefined; let sparkline: BigValueSparkline | undefined;
if (value.sparkline) { if (value.sparkline) {

View File

@ -7,6 +7,7 @@ import {
FieldConfigSource, FieldConfigSource,
ThresholdsMode, ThresholdsMode,
} from '@grafana/data'; } from '@grafana/data';
import { PanelOptionsEditorBuilder } from '@grafana/data/src/utils/OptionsUIBuilders';
// Structure copied from angular // Structure copied from angular
export interface StatPanelOptions extends SingleStatBaseOptions { export interface StatPanelOptions extends SingleStatBaseOptions {
@ -49,6 +50,20 @@ export const standardFieldConfig: FieldConfigSource = {
overrides: [], overrides: [],
}; };
export function addStandardSingleValueOptions(builder: PanelOptionsEditorBuilder) {
builder.addRadio({
id: 'values',
name: 'Show',
description: 'Calculate a single value per colum or series or show each row',
settings: {
options: [
{ value: false, label: 'Calculate' },
{ value: true, label: 'All values' },
],
},
});
}
export const defaults: StatPanelOptions = { export const defaults: StatPanelOptions = {
graphMode: BigValueGraphMode.Area, graphMode: BigValueGraphMode.Area,
colorMode: BigValueColorMode.Value, colorMode: BigValueColorMode.Value,

View File

@ -8,20 +8,18 @@ import { Options } from './types';
interface Props extends PanelProps<Options> {} interface Props extends PanelProps<Options> {}
const paddingBottom = 16;
export class TablePanel extends Component<Props> { export class TablePanel extends Component<Props> {
constructor(props: Props) { constructor(props: Props) {
super(props); super(props);
} }
render() { render() {
const { data, height, width } = this.props; const { data, height, width, options } = this.props;
if (data.series.length < 1) { if (data.series.length < 1) {
return <div>No Table Data...</div>; return <div>No Table Data...</div>;
} }
return <Table height={height - paddingBottom} width={width} data={data.series[0]} />; return <Table height={height} width={width} data={data.series[0]} noHeader={!options.showHeader} />;
} }
} }

View File

@ -4,7 +4,7 @@ import { Options, defaults } from './types';
export const plugin = new PanelPlugin<Options>(TablePanel) export const plugin = new PanelPlugin<Options>(TablePanel)
.setDefaults(defaults) .setDefaults(defaults)
.setCustomFieldConfigEditor(builder => { .setCustomFieldOptions(builder => {
builder builder
.addNumberInput({ .addNumberInput({
id: 'width', id: 'width',
@ -20,7 +20,6 @@ export const plugin = new PanelPlugin<Options>(TablePanel)
id: 'displayMode', id: 'displayMode',
name: 'Cell display mode', name: 'Cell display mode',
description: 'Color value, background, show as gauge, etc', description: 'Color value, background, show as gauge, etc',
settings: { settings: {
options: [ options: [
{ value: 'auto', label: 'Auto' }, { value: 'auto', label: 'Auto' },
@ -31,7 +30,7 @@ export const plugin = new PanelPlugin<Options>(TablePanel)
}, },
}); });
}) })
.setOptionsEditor(builder => { .setPanelOptions(builder => {
builder.addBooleanSwitch({ builder.addBooleanSwitch({
id: 'showHeader', id: 'showHeader',
name: 'Show header', name: 'Show header',