TimeSeries: Add option to disconnect values (#70097)

Co-authored-by: nmarrs <nathanielmarrs@gmail.com>
Co-authored-by: Leon Sorokin <leeoniya@gmail.com>
This commit is contained in:
Drew Slobodnjak
2023-06-21 19:26:15 -07:00
committed by GitHub
parent d3bb9fbbaf
commit b6d4b701b9
6 changed files with 528 additions and 203 deletions

View File

@@ -0,0 +1,31 @@
import React from 'react';
import { FieldOverrideEditorProps, SelectableValue } from '@grafana/data';
import { HorizontalGroup, RadioButtonGroup } from '@grafana/ui';
import { InputPrefix, NullsThresholdInput } from './NullsThresholdInput';
const DISCONNECT_OPTIONS: Array<SelectableValue<boolean | number>> = [
{
label: 'Never',
value: false,
},
{
label: 'Threshold',
value: 3600000, // 1h
},
];
type Props = FieldOverrideEditorProps<boolean | number, unknown>;
export const InsertNullsEditor = ({ value, onChange }: Props) => {
const isThreshold = typeof value === 'number';
DISCONNECT_OPTIONS[1].value = isThreshold ? value : 3600000; // 1h
return (
<HorizontalGroup>
<RadioButtonGroup value={value} options={DISCONNECT_OPTIONS} onChange={onChange} />
{isThreshold && <NullsThresholdInput value={value} onChange={onChange} inputPrefix={InputPrefix.GreaterThan} />}
</HorizontalGroup>
);
};

View File

@@ -0,0 +1,57 @@
import React from 'react';
import { rangeUtil } from '@grafana/data';
import { Input } from '@grafana/ui';
export enum InputPrefix {
LessThan = 'lessthan',
GreaterThan = 'greaterthan',
}
type Props = { value: number; onChange: (value?: number | boolean | undefined) => void; inputPrefix?: InputPrefix };
export const NullsThresholdInput = ({ value, onChange, inputPrefix }: Props) => {
const formattedTime = rangeUtil.secondsToHms(value / 1000);
const checkAndUpdate = (txt: string) => {
let val: boolean | number = false;
if (txt) {
try {
val = rangeUtil.intervalToMs(txt);
} catch (err) {
console.warn('ERROR', err);
}
}
onChange(val);
};
const handleEnterKey = (e: React.KeyboardEvent<HTMLInputElement>) => {
if (e.key !== 'Enter') {
return;
}
checkAndUpdate(e.currentTarget.value);
};
const handleBlur = (e: React.FocusEvent<HTMLInputElement>) => {
checkAndUpdate(e.currentTarget.value);
};
const prefix =
inputPrefix === InputPrefix.GreaterThan ? (
<div>&gt;</div>
) : inputPrefix === InputPrefix.LessThan ? (
<div>&lt;</div>
) : null;
return (
<Input
autoFocus={false}
placeholder="never"
width={10}
defaultValue={formattedTime}
onKeyDown={handleEnterKey}
onBlur={handleBlur}
prefix={prefix}
spellCheck={false}
/>
);
};

View File

@@ -1,7 +1,9 @@
import React from 'react';
import { FieldOverrideEditorProps, rangeUtil, SelectableValue } from '@grafana/data';
import { HorizontalGroup, Input, RadioButtonGroup } from '@grafana/ui';
import { FieldOverrideEditorProps, SelectableValue } from '@grafana/data';
import { HorizontalGroup, RadioButtonGroup } from '@grafana/ui';
import { InputPrefix, NullsThresholdInput } from './NullsThresholdInput';
const GAPS_OPTIONS: Array<SelectableValue<boolean | number>> = [
{
@@ -22,47 +24,12 @@ type Props = FieldOverrideEditorProps<boolean | number, unknown>;
export const SpanNullsEditor = ({ value, onChange }: Props) => {
const isThreshold = typeof value === 'number';
const formattedTime = isThreshold ? rangeUtil.secondsToHms(value / 1000) : undefined;
GAPS_OPTIONS[2].value = isThreshold ? value : 3600000; // 1h
const checkAndUpdate = (txt: string) => {
let val: boolean | number = false;
if (txt) {
try {
val = rangeUtil.intervalToSeconds(txt) * 1000;
} catch (err) {
console.warn('ERROR', err);
}
}
onChange(val);
};
const handleEnterKey = (e: React.KeyboardEvent<HTMLInputElement>) => {
if (e.key !== 'Enter') {
return;
}
checkAndUpdate(e.currentTarget.value);
};
const handleBlur = (e: React.FocusEvent<HTMLInputElement>) => {
checkAndUpdate(e.currentTarget.value);
};
return (
<HorizontalGroup>
<RadioButtonGroup value={value} options={GAPS_OPTIONS} onChange={onChange} />
{isThreshold && (
<Input
autoFocus={false}
placeholder="never"
width={10}
defaultValue={formattedTime}
onKeyDown={handleEnterKey}
onBlur={handleBlur}
prefix={<div>&lt;</div>}
spellCheck={false}
/>
)}
{isThreshold && <NullsThresholdInput value={value} onChange={onChange} inputPrefix={InputPrefix.LessThan} />}
</HorizontalGroup>
);
};

View File

@@ -20,6 +20,7 @@ import {
} from '@grafana/schema';
import { graphFieldOptions, commonOptionsBuilder } from '@grafana/ui';
import { InsertNullsEditor } from './InsertNullsEditor';
import { LineStyleEditor } from './LineStyleEditor';
import { SpanNullsEditor } from './SpanNullsEditor';
import { ThresholdsStyleEditor } from './ThresholdsStyleEditor';
@@ -74,7 +75,7 @@ export function getGraphFieldConfig(cfg: GraphFieldConfig): SetFieldConfigOption
settings: {
options: graphFieldOptions.lineInterpolation,
},
showIf: (c) => c.drawStyle === GraphDrawStyle.Line,
showIf: (config) => config.drawStyle === GraphDrawStyle.Line,
})
.addRadio({
path: 'barAlignment',
@@ -84,7 +85,7 @@ export function getGraphFieldConfig(cfg: GraphFieldConfig): SetFieldConfigOption
settings: {
options: graphFieldOptions.barAlignment,
},
showIf: (c) => c.drawStyle === GraphDrawStyle.Bars,
showIf: (config) => config.drawStyle === GraphDrawStyle.Bars,
})
.addSliderInput({
path: 'lineWidth',
@@ -97,7 +98,7 @@ export function getGraphFieldConfig(cfg: GraphFieldConfig): SetFieldConfigOption
step: 1,
ariaLabelForHandle: 'Line width',
},
showIf: (c) => c.drawStyle !== GraphDrawStyle.Points,
showIf: (config) => config.drawStyle !== GraphDrawStyle.Points,
})
.addSliderInput({
path: 'fillOpacity',
@@ -110,7 +111,7 @@ export function getGraphFieldConfig(cfg: GraphFieldConfig): SetFieldConfigOption
step: 1,
ariaLabelForHandle: 'Fill opacity',
},
showIf: (c) => c.drawStyle !== GraphDrawStyle.Points,
showIf: (config) => config.drawStyle !== GraphDrawStyle.Points,
})
.addRadio({
path: 'gradientMode',
@@ -120,7 +121,7 @@ export function getGraphFieldConfig(cfg: GraphFieldConfig): SetFieldConfigOption
settings: {
options: graphFieldOptions.fillGradient,
},
showIf: (c) => c.drawStyle !== GraphDrawStyle.Points,
showIf: (config) => config.drawStyle !== GraphDrawStyle.Points,
})
.addFieldNamePicker({
path: 'fillBelowTo',
@@ -136,11 +137,11 @@ export function getGraphFieldConfig(cfg: GraphFieldConfig): SetFieldConfigOption
path: 'lineStyle',
name: 'Line style',
category: categoryStyles,
showIf: (c) => c.drawStyle === GraphDrawStyle.Line,
showIf: (config) => config.drawStyle === GraphDrawStyle.Line,
editor: LineStyleEditor,
override: LineStyleEditor,
process: identityOverrideProcessor,
shouldApply: (f) => f.type === FieldType.number,
shouldApply: (field) => field.type === FieldType.number,
})
.addCustomEditor<void, boolean>({
id: 'spanNulls',
@@ -150,8 +151,20 @@ export function getGraphFieldConfig(cfg: GraphFieldConfig): SetFieldConfigOption
defaultValue: false,
editor: SpanNullsEditor,
override: SpanNullsEditor,
showIf: (c) => c.drawStyle === GraphDrawStyle.Line,
shouldApply: (f) => f.type !== FieldType.time,
showIf: (config) => config.drawStyle === GraphDrawStyle.Line,
shouldApply: (field) => field.type !== FieldType.time,
process: identityOverrideProcessor,
})
.addCustomEditor<void, boolean>({
id: 'insertNulls',
path: 'insertNulls',
name: 'Disconnect values',
category: categoryStyles,
defaultValue: false,
editor: InsertNullsEditor,
override: InsertNullsEditor,
showIf: (config) => config.drawStyle === GraphDrawStyle.Line,
shouldApply: (field) => field.type !== FieldType.time,
process: identityOverrideProcessor,
})
.addRadio({
@@ -162,7 +175,7 @@ export function getGraphFieldConfig(cfg: GraphFieldConfig): SetFieldConfigOption
settings: {
options: graphFieldOptions.showPoints,
},
showIf: (c) => c.drawStyle !== GraphDrawStyle.Points,
showIf: (config) => config.drawStyle !== GraphDrawStyle.Points,
})
.addSliderInput({
path: 'pointSize',
@@ -175,7 +188,7 @@ export function getGraphFieldConfig(cfg: GraphFieldConfig): SetFieldConfigOption
step: 1,
ariaLabelForHandle: 'Point size',
},
showIf: (c) => c.showPoints !== VisibilityMode.Never || c.drawStyle === GraphDrawStyle.Points,
showIf: (config) => config.showPoints !== VisibilityMode.Never || config.drawStyle === GraphDrawStyle.Points,
});
commonOptionsBuilder.addStackingConfig(builder, cfg.stacking, categoryStyles);