Merge branch 'fastfrwrd/112977-sparkline-annotations' into fastfrwrd/gauge-bug-bash
This commit is contained in:
@@ -3,7 +3,7 @@ import { isEmpty } from 'lodash';
|
||||
import { DataFrameView } from '../dataframe/DataFrameView';
|
||||
import { getTimeField } from '../dataframe/processDataFrame';
|
||||
import { GrafanaTheme2 } from '../themes/types';
|
||||
import { reduceField, ReducerID } from '../transformations/fieldReducer';
|
||||
import { isReducerID, reduceField, ReducerID } from '../transformations/fieldReducer';
|
||||
import { getFieldMatcher } from '../transformations/matchers';
|
||||
import { FieldMatcherID } from '../transformations/matchers/ids';
|
||||
import { ScopedVars } from '../types/ScopedVars';
|
||||
@@ -43,6 +43,7 @@ export interface FieldSparkline {
|
||||
x?: Field; // if this does not exist, use the index
|
||||
timeRange?: TimeRange; // Optionally force an absolute time
|
||||
highlightIndex?: number;
|
||||
highlightLine?: number;
|
||||
}
|
||||
|
||||
export interface FieldDisplay {
|
||||
@@ -72,6 +73,76 @@ export interface GetFieldDisplayValuesOptions {
|
||||
|
||||
export const DEFAULT_FIELD_DISPLAY_VALUES_LIMIT = 25;
|
||||
|
||||
interface SparklineHighlightPoint {
|
||||
type: 'point';
|
||||
xIdx: number;
|
||||
}
|
||||
|
||||
interface SparklineHighlightLine {
|
||||
type: 'line';
|
||||
y: number;
|
||||
}
|
||||
|
||||
export function getSparklineHighlight(
|
||||
sparkline: FieldSparkline,
|
||||
calc: ReducerID
|
||||
): SparklineHighlightPoint | SparklineHighlightLine | void {
|
||||
switch (calc) {
|
||||
case ReducerID.last:
|
||||
return { type: 'point', xIdx: sparkline.y.values.length - 1 };
|
||||
case ReducerID.first:
|
||||
return { type: 'point', xIdx: 0 };
|
||||
case ReducerID.lastNotNull: {
|
||||
for (let k = sparkline.y.values.length - 1; k >= 0; k--) {
|
||||
const v = sparkline.y.values[k];
|
||||
if (v !== null && v !== undefined && !Number.isNaN(v)) {
|
||||
return { type: 'point', xIdx: k };
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
case ReducerID.firstNotNull: {
|
||||
for (let k = 0; k < sparkline.y.values.length; k++) {
|
||||
const v = sparkline.y.values[k];
|
||||
if (v !== null && v !== undefined && !Number.isNaN(v)) {
|
||||
return { type: 'point', xIdx: k };
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
case ReducerID.min: {
|
||||
let minIdx = -1;
|
||||
let prevMin = Infinity;
|
||||
for (let k = 0; k < sparkline.y.values.length; k++) {
|
||||
const v = sparkline.y.values[k];
|
||||
if (v !== null && v !== undefined && !Number.isNaN(v) && v < prevMin) {
|
||||
prevMin = v;
|
||||
minIdx = k;
|
||||
}
|
||||
}
|
||||
return minIdx >= 0 ? { type: 'point', xIdx: minIdx } : undefined;
|
||||
}
|
||||
case ReducerID.max: {
|
||||
let maxIdx = -1;
|
||||
let prevMax = -Infinity;
|
||||
for (let k = 0; k < sparkline.y.values.length; k++) {
|
||||
const v = sparkline.y.values[k];
|
||||
if (v !== null && v !== undefined && !Number.isNaN(v) && v > prevMax) {
|
||||
prevMax = v;
|
||||
maxIdx = k;
|
||||
}
|
||||
}
|
||||
return maxIdx >= 0 ? { type: 'point', xIdx: maxIdx } : undefined;
|
||||
}
|
||||
case ReducerID.mean:
|
||||
return { type: 'line', y: reduceField({ field: sparkline.y, reducers: [ReducerID.mean] }).mean };
|
||||
case ReducerID.median:
|
||||
return { type: 'line', y: reduceField({ field: sparkline.y, reducers: [ReducerID.median] }).median };
|
||||
default:
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
export const getFieldDisplayValues = (options: GetFieldDisplayValuesOptions): FieldDisplay[] => {
|
||||
const { replaceVariables, reduceOptions, timeZone, theme } = options;
|
||||
const calcs = reduceOptions.calcs.length ? reduceOptions.calcs : [ReducerID.last];
|
||||
@@ -190,62 +261,16 @@ export const getFieldDisplayValues = (options: GetFieldDisplayValuesOptions): Fi
|
||||
y: dataFrame.fields[i],
|
||||
x: timeField,
|
||||
};
|
||||
let highlightIdx: number | undefined = (() => {
|
||||
switch (calc) {
|
||||
case ReducerID.last:
|
||||
return sparkline.y.values.length - 1;
|
||||
case ReducerID.first:
|
||||
return 0;
|
||||
// TODO: #112977 enable more reducers for highlight index
|
||||
// case ReducerID.lastNotNull: {
|
||||
// for (let k = sparkline.y.values.length - 1; k >= 0; k--) {
|
||||
// const v = sparkline.y.values[k];
|
||||
// if (v !== null && v !== undefined && !Number.isNaN(v)) {
|
||||
// return k;
|
||||
// }
|
||||
// }
|
||||
// return;
|
||||
// }
|
||||
// case ReducerID.firstNotNull: {
|
||||
// for (let k = 0; k < sparkline.y.values.length; k++) {
|
||||
// const v = sparkline.y.values[k];
|
||||
// if (v !== null && v !== undefined && !Number.isNaN(v)) {
|
||||
// return k;
|
||||
// }
|
||||
// }
|
||||
// return;
|
||||
// }
|
||||
// case ReducerID.min: {
|
||||
// let minIdx = -1;
|
||||
// let prevMin = Infinity;
|
||||
// for (let k = 0; k < sparkline.y.values.length; k++) {
|
||||
// const v = sparkline.y.values[k];
|
||||
// if (v !== null && v !== undefined && !Number.isNaN(v) && v < prevMin) {
|
||||
// prevMin = v;
|
||||
// minIdx = k;
|
||||
// }
|
||||
// }
|
||||
// return minIdx >= 0 ? minIdx : undefined;
|
||||
// }
|
||||
// case ReducerID.max: {
|
||||
// let maxIdx = -1;
|
||||
// let prevMax = -Infinity;
|
||||
// for (let k = 0; k < sparkline.y.values.length; k++) {
|
||||
// const v = sparkline.y.values[k];
|
||||
// if (v !== null && v !== undefined && !Number.isNaN(v) && v > prevMax) {
|
||||
// prevMax = v;
|
||||
// maxIdx = k;
|
||||
// }
|
||||
// }
|
||||
// return maxIdx >= 0 ? maxIdx : undefined;
|
||||
// }
|
||||
default:
|
||||
return;
|
||||
if (isReducerID(calc)) {
|
||||
const sparklineHighlight = getSparklineHighlight(sparkline, calc);
|
||||
switch (sparklineHighlight?.type) {
|
||||
case 'point':
|
||||
sparkline.highlightIndex = sparklineHighlight.xIdx;
|
||||
break;
|
||||
case 'line':
|
||||
sparkline.highlightLine = sparklineHighlight.y;
|
||||
break;
|
||||
}
|
||||
})();
|
||||
|
||||
if (typeof highlightIdx === 'number') {
|
||||
sparkline.highlightIndex = highlightIdx;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -67,7 +67,7 @@ export const RadialSparkline = memo(
|
||||
|
||||
return (
|
||||
<div style={{ position: 'absolute', top: topPos }}>
|
||||
<Sparkline height={height} width={width} sparkline={sparkline} theme={theme} config={config} />
|
||||
<Sparkline height={height} width={width} sparkline={sparkline} theme={theme} config={config} showHighlights />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -14,18 +14,18 @@ export interface SparklineProps extends Themeable2 {
|
||||
height: number;
|
||||
config?: FieldConfig<GraphFieldConfig>;
|
||||
sparkline: FieldSparkline;
|
||||
showHighlights?: boolean;
|
||||
}
|
||||
|
||||
const SparklineFn: React.FC<SparklineProps> = memo((props) => {
|
||||
const { sparkline, config: fieldConfig, theme, width, height } = props;
|
||||
|
||||
const { frame: alignedDataFrame, warning } = prepareSeries(sparkline, fieldConfig);
|
||||
export const SparklineFn: React.FC<SparklineProps> = memo((props) => {
|
||||
const { sparkline, config: fieldConfig, theme, width, height, showHighlights } = props;
|
||||
const { frame: alignedDataFrame, warning } = prepareSeries(sparkline, theme, fieldConfig, showHighlights);
|
||||
if (warning) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const data = preparePlotData2(alignedDataFrame, getStackingGroups(alignedDataFrame));
|
||||
const configBuilder = prepareConfig(sparkline, alignedDataFrame, theme);
|
||||
const configBuilder = prepareConfig(sparkline, alignedDataFrame, theme, showHighlights);
|
||||
|
||||
return <UPlotChart data={data} config={configBuilder} width={width} height={height} />;
|
||||
});
|
||||
|
||||
@@ -2,6 +2,7 @@ import { Range } from 'uplot';
|
||||
|
||||
import {
|
||||
applyNullInsertThreshold,
|
||||
// colorManipulator,
|
||||
DataFrame,
|
||||
FieldConfig,
|
||||
FieldSparkline,
|
||||
@@ -22,6 +23,7 @@ import {
|
||||
VisibilityMode,
|
||||
ScaleDirection,
|
||||
ScaleOrientation,
|
||||
// FieldColorModeId,
|
||||
} from '@grafana/schema';
|
||||
|
||||
import { UPlotConfigBuilder } from '../uPlot/config/UPlotConfigBuilder';
|
||||
@@ -112,8 +114,7 @@ export function getYRange(alignedFrame: DataFrame): Range.MinMax {
|
||||
return [roundedMin, roundedMax];
|
||||
}
|
||||
|
||||
// TODO: #112977 enable highlight index
|
||||
// const HIGHLIGHT_IDX_POINT_SIZE = 6;
|
||||
const HIGHLIGHT_IDX_POINT_SIZE = 6;
|
||||
|
||||
const defaultConfig: GraphFieldConfig = {
|
||||
drawStyle: GraphDrawStyle.Line,
|
||||
@@ -124,7 +125,9 @@ const defaultConfig: GraphFieldConfig = {
|
||||
|
||||
export const prepareSeries = (
|
||||
sparkline: FieldSparkline,
|
||||
fieldConfig?: FieldConfig<GraphFieldConfig>
|
||||
_theme: GrafanaTheme2,
|
||||
fieldConfig?: FieldConfig<GraphFieldConfig>,
|
||||
_showHighlights?: boolean
|
||||
): { frame: DataFrame; warning?: string } => {
|
||||
const frame = nullToValue(preparePlotFrame(sparkline, fieldConfig));
|
||||
if (frame.fields.some((f) => f.values.length <= 1)) {
|
||||
@@ -136,16 +139,41 @@ export const prepareSeries = (
|
||||
frame,
|
||||
};
|
||||
}
|
||||
// TODO:rgb(24, 24, 24) will address this.
|
||||
// if (showHighlights && typeof sparkline.highlightLine === 'number') {
|
||||
// const highlightY = sparkline.highlightLine;
|
||||
// const colorMode = getFieldColorModeForField(sparkline.y);
|
||||
// const seriesColor = colorMode.getCalculator(sparkline.y, theme)(highlightY, 0);
|
||||
// frame.fields.push({
|
||||
// name: 'highlightLine',
|
||||
// type: FieldType.number,
|
||||
// values: new Array(frame.length).fill(highlightY),
|
||||
// config: {
|
||||
// color: {
|
||||
// mode: FieldColorModeId.Fixed,
|
||||
// fixedColor: colorManipulator.lighten(seriesColor, 0.5),
|
||||
// },
|
||||
// custom: {
|
||||
// lineStyle: {
|
||||
// fill: 'dash',
|
||||
// dash: [5, 2],
|
||||
// },
|
||||
// },
|
||||
// },
|
||||
// state: {},
|
||||
// });
|
||||
// }
|
||||
return { frame };
|
||||
};
|
||||
|
||||
export const prepareConfig = (
|
||||
sparkline: FieldSparkline,
|
||||
dataFrame: DataFrame,
|
||||
theme: GrafanaTheme2
|
||||
theme: GrafanaTheme2,
|
||||
showHighlights?: boolean
|
||||
): UPlotConfigBuilder => {
|
||||
const builder = new UPlotConfigBuilder();
|
||||
// const rangePad = HIGHLIGHT_IDX_POINT_SIZE / 2;
|
||||
const rangePad = HIGHLIGHT_IDX_POINT_SIZE / 2;
|
||||
|
||||
builder.setCursor({
|
||||
show: false,
|
||||
@@ -206,13 +234,14 @@ export const prepareConfig = (
|
||||
|
||||
const colorMode = getFieldColorModeForField(field);
|
||||
const seriesColor = colorMode.getCalculator(field, theme)(0, 0);
|
||||
// TODO: #112977 enable highlight index and adjust padding accordingly
|
||||
// const hasHighlightIndex = typeof sparkline.highlightIndex === 'number';
|
||||
// if (hasHighlightIndex) {
|
||||
// builder.setPadding([rangePad, rangePad, rangePad, rangePad]);
|
||||
// }
|
||||
|
||||
const hasHighlightIndex = showHighlights && typeof sparkline.highlightIndex === 'number';
|
||||
if (hasHighlightIndex) {
|
||||
builder.setPadding([rangePad, rangePad, rangePad, rangePad]);
|
||||
}
|
||||
|
||||
const pointsMode =
|
||||
customConfig.drawStyle === GraphDrawStyle.Points // || hasHighlightIndex
|
||||
customConfig.drawStyle === GraphDrawStyle.Points || hasHighlightIndex
|
||||
? VisibilityMode.Always
|
||||
: customConfig.showPoints;
|
||||
|
||||
@@ -227,9 +256,8 @@ export const prepareConfig = (
|
||||
lineWidth: customConfig.lineWidth,
|
||||
lineInterpolation: customConfig.lineInterpolation,
|
||||
showPoints: pointsMode,
|
||||
// TODO: #112977 enable highlight index
|
||||
pointSize: /* hasHighlightIndex ? HIGHLIGHT_IDX_POINT_SIZE : */ customConfig.pointSize,
|
||||
// pointsFilter: hasHighlightIndex ? [sparkline.highlightIndex!] : undefined,
|
||||
pointSize: hasHighlightIndex ? HIGHLIGHT_IDX_POINT_SIZE : customConfig.pointSize,
|
||||
pointsFilter: hasHighlightIndex ? [sparkline.highlightIndex!] : undefined,
|
||||
fillOpacity: customConfig.fillOpacity,
|
||||
fillColor: customConfig.fillColor,
|
||||
lineStyle: customConfig.lineStyle,
|
||||
|
||||
Reference in New Issue
Block a user