TimeSeriesPanel: Allow threshold indicators without change handler (#60575)

This commit is contained in:
Gilles De Mey
2022-12-21 16:18:35 +01:00
committed by GitHub
parent 44232569a8
commit 16c6f223ba
8 changed files with 53 additions and 68 deletions

View File

@@ -45,6 +45,13 @@ export interface PanelContext {
*/
canEditThresholds?: boolean;
/**
* Shows threshold indicators on the right-hand side of the panel
*
* @alpha -- experimental
*/
showThresholds?: boolean;
/**
* Called when a panel wants to change default thresholds configuration
*

View File

@@ -59,43 +59,6 @@ export class QueryRows extends PureComponent<Props> {
);
};
onChangeThreshold = (thresholds: ThresholdsConfig, index: number) => {
const { queries, onQueriesChange } = this.props;
const referencedRefId = queries[index].refId;
onQueriesChange(
queries.map((query) => {
if (!isExpressionQuery(query.model)) {
return query;
}
if (query.model.conditions && query.model.conditions[0].query.params[0] === referencedRefId) {
return {
...query,
model: {
...query.model,
conditions: query.model.conditions.map((condition, conditionIndex) => {
// Only update the first condition for a given refId.
if (condition.query.params[0] === referencedRefId && conditionIndex === 0) {
return {
...condition,
evaluator: {
...condition.evaluator,
params: [parseFloat(thresholds.steps[1].value.toPrecision(3))],
},
};
}
return condition;
}),
},
};
}
return query;
})
);
};
onChangeDataSource = (settings: DataSourceInstanceSettings, index: number) => {
const { queries, onQueriesChange } = this.props;
@@ -253,7 +216,6 @@ export class QueryRows extends PureComponent<Props> {
onDuplicateQuery={this.props.onDuplicateQuery}
onChangeTimeRange={this.onChangeTimeRange}
thresholds={thresholdByRefId[query.refId]}
onChangeThreshold={this.onChangeThreshold}
onRunQueries={this.props.onRunQueries}
condition={this.props.condition}
onSetCondition={this.props.onSetCondition}

View File

@@ -1,5 +1,5 @@
import { css } from '@emotion/css';
import { cloneDeep } from 'lodash';
import { cloneDeep, noop } from 'lodash';
import React, { FC, useState } from 'react';
import {
@@ -39,7 +39,7 @@ interface Props {
onRunQueries: () => void;
index: number;
thresholds: ThresholdsConfig;
onChangeThreshold: (thresholds: ThresholdsConfig, index: number) => void;
onChangeThreshold?: (thresholds: ThresholdsConfig, index: number) => void;
condition: string | null;
onSetCondition: (refId: string) => void;
}
@@ -141,7 +141,7 @@ export const QueryWrapper: FC<Props> = ({
changePanel={changePluginId}
currentPanel={pluginId}
thresholds={thresholds}
onThresholdsChange={(thresholds) => onChangeThreshold(thresholds, index)}
onThresholdsChange={onChangeThreshold ? (thresholds) => onChangeThreshold(thresholds, index) : noop}
/>
) : null
}

View File

@@ -52,7 +52,8 @@ export const VizWrapper: FC<Props> = ({ data, currentPanel, changePanel, onThres
const context: PanelContext = useMemo(
() => ({
eventBus: appEvents,
canEditThresholds: true,
canEditThresholds: false,
showThresholds: true,
onThresholdsChange: onThresholdsChange,
}),
[onThresholdsChange]

View File

@@ -38,7 +38,8 @@ export const CandlestickPanel: React.FC<CandlestickPanelProps> = ({
onChangeTimeRange,
replaceVariables,
}) => {
const { sync, canAddAnnotations, onThresholdsChange, canEditThresholds, onSplitOpen } = usePanelContext();
const { sync, canAddAnnotations, onThresholdsChange, canEditThresholds, showThresholds, onSplitOpen } =
usePanelContext();
const getFieldLinks = (field: Field, rowIndex: number) => {
return getFieldLinksForExplore({ field, rowIndex, splitOpenFn: onSplitOpen, range: timeRange });
@@ -320,11 +321,11 @@ export const CandlestickPanel: React.FC<CandlestickPanelProps> = ({
/>
)}
{canEditThresholds && onThresholdsChange && (
{((canEditThresholds && onThresholdsChange) || showThresholds) && (
<ThresholdControlsPlugin
config={config}
fieldConfig={fieldConfig}
onThresholdsChange={onThresholdsChange}
onThresholdsChange={canEditThresholds ? onThresholdsChange : undefined}
/>
)}

View File

@@ -30,7 +30,8 @@ export const TimeSeriesPanel: React.FC<TimeSeriesPanelProps> = ({
replaceVariables,
id,
}) => {
const { sync, canAddAnnotations, onThresholdsChange, canEditThresholds, onSplitOpen } = usePanelContext();
const { sync, canAddAnnotations, onThresholdsChange, canEditThresholds, showThresholds, onSplitOpen } =
usePanelContext();
const getFieldLinks = (field: Field, rowIndex: number) => {
return getFieldLinksForExplore({ field, rowIndex, splitOpenFn: onSplitOpen, range: timeRange });
@@ -141,11 +142,11 @@ export const TimeSeriesPanel: React.FC<TimeSeriesPanelProps> = ({
/>
)}
{canEditThresholds && onThresholdsChange && (
{((canEditThresholds && onThresholdsChange) || showThresholds) && (
<ThresholdControlsPlugin
config={config}
fieldConfig={fieldConfig}
onThresholdsChange={onThresholdsChange}
onThresholdsChange={canEditThresholds ? onThresholdsChange : undefined}
/>
)}

View File

@@ -11,7 +11,7 @@ const GUTTER_SIZE = 60;
interface ThresholdControlsPluginProps {
config: UPlotConfigBuilder;
fieldConfig: FieldConfigSource;
onThresholdsChange: (thresholds: ThresholdsConfig) => void;
onThresholdsChange?: (thresholds: ThresholdsConfig) => void;
}
export const ThresholdControlsPlugin: React.FC<ThresholdControlsPluginProps> = ({
@@ -59,15 +59,10 @@ export const ThresholdControlsPlugin: React.FC<ThresholdControlsPluginProps> = (
const height = plot.bbox.height / window.devicePixelRatio;
const handle = (
<ThresholdDragHandle
key={`${step.value}-${i}`}
step={step}
y={yPos}
dragBounds={{ top: 0, bottom: height }}
mapPositionToValue={(y) => plot.posToVal(y, scale)}
formatValue={(v) => getValueFormat(scale)(v, decimals).text}
onChange={(value) => {
const isEditable = typeof onThresholdsChange === 'function';
const onChange = isEditable
? (value: number) => {
const nextSteps = [
...thresholds.steps.slice(0, i),
...thresholds.steps.slice(i + 1),
@@ -78,7 +73,18 @@ export const ThresholdControlsPlugin: React.FC<ThresholdControlsPluginProps> = (
...thresholds,
steps: nextSteps,
});
}}
}
: undefined;
const handle = (
<ThresholdDragHandle
key={`${step.value}-${i}`}
step={step}
y={yPos}
dragBounds={{ top: 0, bottom: height }}
mapPositionToValue={(y) => plot.posToVal(y, scale)}
formatValue={(v) => getValueFormat(scale)(v, decimals).text}
onChange={onChange}
/>
);

View File

@@ -1,4 +1,5 @@
import { css } from '@emotion/css';
import { noop } from 'lodash';
import React, { useMemo, useState } from 'react';
import Draggable, { DraggableBounds } from 'react-draggable';
@@ -12,7 +13,7 @@ interface ThresholdDragHandleProps {
y: number;
dragBounds: DraggableBounds;
mapPositionToValue: (y: number) => number;
onChange: (value: number) => void;
onChange?: (value: number) => void;
formatValue: (value: number) => string;
}
@@ -46,7 +47,8 @@ export const ThresholdDragHandle: React.FC<ThresholdDragHandleProps> = ({
yPos = dragBounds.top ?? y;
}
const styles = useStyles2((theme) => getStyles(theme, step, outOfBounds));
const disabled = typeof onChange !== 'function';
const styles = useStyles2((theme) => getStyles(theme, step, outOfBounds, disabled));
const [currentValue, setCurrentValue] = useState(step.value);
const textColor = useMemo(() => {
@@ -57,11 +59,16 @@ export const ThresholdDragHandle: React.FC<ThresholdDragHandleProps> = ({
<Draggable
axis="y"
grid={[1, 1]}
onStop={(_e, d) => {
onChange(mapPositionToValue(d.lastY));
// as of https://github.com/react-grid-layout/react-draggable/issues/390#issuecomment-623237835
return false;
}}
disabled={disabled}
onStop={
disabled
? noop
: (_e, d) => {
onChange(mapPositionToValue(d.lastY));
// as of https://github.com/react-grid-layout/react-draggable/issues/390#issuecomment-623237835
return false;
}
}
onDrag={(_e, d) => setCurrentValue(mapPositionToValue(d.lastY))}
position={{ x: 0, y: yPos }}
bounds={dragBounds}
@@ -75,7 +82,7 @@ export const ThresholdDragHandle: React.FC<ThresholdDragHandleProps> = ({
ThresholdDragHandle.displayName = 'ThresholdDragHandle';
const getStyles = (theme: GrafanaTheme2, step: Threshold, outOfBounds: OutOfBounds) => {
const getStyles = (theme: GrafanaTheme2, step: Threshold, outOfBounds: OutOfBounds, disabled?: boolean) => {
const mainColor = theme.visualization.getColorByName(step.color);
const arrowStyles = getArrowStyles(outOfBounds);
const isOutOfBounds = outOfBounds !== 'none';
@@ -90,7 +97,7 @@ const getStyles = (theme: GrafanaTheme2, step: Threshold, outOfBounds: OutOfBoun
height: 18px;
margin-top: -9px;
border-color: ${mainColor};
cursor: grab;
cursor: ${disabled ? 'initial' : 'grab'};
border-top-right-radius: ${theme.shape.borderRadius(1)};
border-bottom-right-radius: ${theme.shape.borderRadius(1)};
${isOutOfBounds &&