mirror of https://github.com/grafana/grafana.git
test: add test coverage for util methods
This commit is contained in:
parent
8dcb74565c
commit
77a649d4ed
|
@ -32,9 +32,7 @@ export class UnthemedTimeSeries extends Component<TimeSeriesProps> {
|
||||||
tweakAxis,
|
tweakAxis,
|
||||||
hoverProximity: options?.tooltip?.hoverProximity,
|
hoverProximity: options?.tooltip?.hoverProximity,
|
||||||
orientation: options?.orientation,
|
orientation: options?.orientation,
|
||||||
xAxisConfig: {
|
xAxisConfig: calculateAnnotationLaneSizes(annotationLanes, options?.annotations),
|
||||||
...calculateAnnotationLaneSizes(annotationLanes, options?.annotations),
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { EventBus, FieldType } from '@grafana/data';
|
import { EventBus, FieldType } from '@grafana/data';
|
||||||
import { getTheme } from '@grafana/ui';
|
import { getTheme } from '@grafana/ui';
|
||||||
|
|
||||||
import { preparePlotConfigBuilder } from './utils';
|
import { calculateAnnotationLaneSizes, preparePlotConfigBuilder, UPLOT_DEFAULT_AXIS_GAP } from './utils';
|
||||||
|
|
||||||
describe('when fill below to option is used', () => {
|
describe('when fill below to option is used', () => {
|
||||||
let eventBus: EventBus;
|
let eventBus: EventBus;
|
||||||
|
@ -272,3 +272,29 @@ describe('when fill below to option is used', () => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('calculateAnnotationLaneSizes', () => {
|
||||||
|
it('should not regress', () => {
|
||||||
|
expect(calculateAnnotationLaneSizes()).toEqual({});
|
||||||
|
expect(calculateAnnotationLaneSizes(6)).toEqual({});
|
||||||
|
expect(calculateAnnotationLaneSizes(0, { multiLane: true })).toEqual({});
|
||||||
|
expect(calculateAnnotationLaneSizes(1, { multiLane: true })).toEqual({});
|
||||||
|
expect(calculateAnnotationLaneSizes(2, { multiLane: false })).toEqual({});
|
||||||
|
});
|
||||||
|
it('should return config to resize x-axis size, gap, and ticks size', () => {
|
||||||
|
expect(calculateAnnotationLaneSizes(2, { multiLane: true })).toEqual({
|
||||||
|
gap: UPLOT_DEFAULT_AXIS_GAP,
|
||||||
|
size: 36,
|
||||||
|
ticks: {
|
||||||
|
size: 19,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
expect(calculateAnnotationLaneSizes(3, { multiLane: true })).toEqual({
|
||||||
|
gap: UPLOT_DEFAULT_AXIS_GAP,
|
||||||
|
size: 43,
|
||||||
|
ticks: {
|
||||||
|
size: 26,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
|
@ -75,8 +75,8 @@ import {
|
||||||
import { ANNOTATION_LANE_SIZE } from '../../../plugins/panel/timeseries/plugins/utils';
|
import { ANNOTATION_LANE_SIZE } from '../../../plugins/panel/timeseries/plugins/utils';
|
||||||
|
|
||||||
// See UPlotAxisBuilder.ts::calculateAxisSize for default axis size calculation
|
// See UPlotAxisBuilder.ts::calculateAxisSize for default axis size calculation
|
||||||
const UPLOT_DEFAULT_AXIS_SIZE = 17;
|
export const UPLOT_DEFAULT_AXIS_SIZE = 17;
|
||||||
const UPLOT_DEFAULT_AXIS_GAP = 5;
|
export const UPLOT_DEFAULT_AXIS_GAP = 5;
|
||||||
|
|
||||||
const defaultFormatter = (v: any, decimals: DecimalCount = 1) => (v == null ? '-' : v.toFixed(decimals));
|
const defaultFormatter = (v: any, decimals: DecimalCount = 1) => (v == null ? '-' : v.toFixed(decimals));
|
||||||
|
|
||||||
|
@ -721,7 +721,7 @@ export function calculateAnnotationLaneSizes(
|
||||||
annotationLanes = 0,
|
annotationLanes = 0,
|
||||||
annotationConfig?: common.VizAnnotations
|
annotationConfig?: common.VizAnnotations
|
||||||
): Pick<AxisProps, 'size' | 'gap' | 'ticks'> {
|
): Pick<AxisProps, 'size' | 'gap' | 'ticks'> {
|
||||||
if (annotationConfig?.multiLane) {
|
if (annotationConfig?.multiLane && annotationLanes > 1) {
|
||||||
const annotationLanesSize = annotationLanes * ANNOTATION_LANE_SIZE;
|
const annotationLanesSize = annotationLanes * ANNOTATION_LANE_SIZE;
|
||||||
// Add an extra lane's worth of height below the annotation lanes in order to show the gridlines through the annotation lanes
|
// Add an extra lane's worth of height below the annotation lanes in order to show the gridlines through the annotation lanes
|
||||||
const axisSize = annotationLanes > 0 ? annotationLanesSize + UPLOT_DEFAULT_AXIS_GAP : 0;
|
const axisSize = annotationLanes > 0 ? annotationLanesSize + UPLOT_DEFAULT_AXIS_GAP : 0;
|
||||||
|
|
|
@ -0,0 +1,155 @@
|
||||||
|
import { createDataFrame, DataFrame, DataTopic, FieldType } from '@grafana/data';
|
||||||
|
|
||||||
|
import { getAnnotationFrames } from './utils';
|
||||||
|
|
||||||
|
describe('getAnnotationFrames', () => {
|
||||||
|
const exemplarFrame = createDataFrame({
|
||||||
|
refId: 'A',
|
||||||
|
name: 'exemplar',
|
||||||
|
meta: {
|
||||||
|
custom: {
|
||||||
|
resultType: 'exemplar',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
fields: [
|
||||||
|
{ name: 'Time', type: FieldType.time, values: [6, 5, 4, 3, 2, 1] },
|
||||||
|
{
|
||||||
|
name: 'Value',
|
||||||
|
type: FieldType.number,
|
||||||
|
values: [30, 10, 40, 90, 14, 21],
|
||||||
|
labels: { le: '6' },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'traceID',
|
||||||
|
type: FieldType.string,
|
||||||
|
values: ['unknown'],
|
||||||
|
labels: { le: '6' },
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
const annotationRegionFrame: DataFrame = {
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
name: 'type',
|
||||||
|
config: {
|
||||||
|
custom: {},
|
||||||
|
},
|
||||||
|
values: ['Milestones'],
|
||||||
|
type: FieldType.string,
|
||||||
|
state: {
|
||||||
|
displayName: null,
|
||||||
|
seriesIndex: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'color',
|
||||||
|
config: {
|
||||||
|
custom: {},
|
||||||
|
},
|
||||||
|
values: ['#F2495C'],
|
||||||
|
type: FieldType.string,
|
||||||
|
state: {
|
||||||
|
displayName: null,
|
||||||
|
seriesIndex: 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'time',
|
||||||
|
config: {
|
||||||
|
custom: {},
|
||||||
|
},
|
||||||
|
values: [1720697881000],
|
||||||
|
type: FieldType.time,
|
||||||
|
state: {
|
||||||
|
displayName: null,
|
||||||
|
seriesIndex: 2,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'timeEnd',
|
||||||
|
config: {
|
||||||
|
custom: {},
|
||||||
|
},
|
||||||
|
values: [1729081505000],
|
||||||
|
type: FieldType.number,
|
||||||
|
state: {
|
||||||
|
displayName: null,
|
||||||
|
seriesIndex: 2,
|
||||||
|
range: {
|
||||||
|
min: 1729081505000,
|
||||||
|
max: 1759857566000,
|
||||||
|
delta: 30776061000,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'title',
|
||||||
|
config: {
|
||||||
|
custom: {},
|
||||||
|
},
|
||||||
|
values: ['0.1.0'],
|
||||||
|
type: FieldType.string,
|
||||||
|
state: {
|
||||||
|
displayName: null,
|
||||||
|
seriesIndex: 3,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'text',
|
||||||
|
config: {
|
||||||
|
custom: {},
|
||||||
|
},
|
||||||
|
values: [true],
|
||||||
|
type: FieldType.boolean,
|
||||||
|
state: {
|
||||||
|
displayName: null,
|
||||||
|
seriesIndex: 4,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'isRegion',
|
||||||
|
config: {
|
||||||
|
custom: {},
|
||||||
|
},
|
||||||
|
values: [true],
|
||||||
|
type: FieldType.boolean,
|
||||||
|
state: {
|
||||||
|
displayName: null,
|
||||||
|
seriesIndex: 6,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
length: 1,
|
||||||
|
meta: {
|
||||||
|
dataTopic: DataTopic.Annotations,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const annotationFrame: DataFrame = {
|
||||||
|
...annotationRegionFrame,
|
||||||
|
fields: [...annotationRegionFrame.fields.filter((f) => f.name !== 'timeEnd')],
|
||||||
|
};
|
||||||
|
const normalFrame = createDataFrame({
|
||||||
|
refId: 'A',
|
||||||
|
fields: [
|
||||||
|
{ name: 'Time', type: FieldType.time, values: [1, 3, 10] },
|
||||||
|
{
|
||||||
|
name: 'One',
|
||||||
|
type: FieldType.number,
|
||||||
|
config: { custom: { insertNulls: 1 }, noValue: '0' },
|
||||||
|
values: [4, 6, 8],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Two',
|
||||||
|
type: FieldType.string,
|
||||||
|
config: { custom: { insertNulls: 1 }, noValue: '0' },
|
||||||
|
values: ['a', 'b', 'c'],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
const frames: DataFrame[] = [exemplarFrame, annotationRegionFrame, annotationFrame, normalFrame];
|
||||||
|
|
||||||
|
it('should filter non-annotation frames', () => {
|
||||||
|
expect(getAnnotationFrames(frames)).toEqual([annotationRegionFrame, annotationFrame]);
|
||||||
|
});
|
||||||
|
});
|
|
@ -1,10 +1,20 @@
|
||||||
import { DataFrame } from '@grafana/data';
|
import { DataFrame, DataTopic } from '@grafana/data';
|
||||||
|
|
||||||
// Annotation points/regions are 5px with 1px of padding
|
// Annotation points/regions are 5px with 1px of padding
|
||||||
export const ANNOTATION_LANE_SIZE = 7;
|
export const ANNOTATION_LANE_SIZE = 7;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Annotation frames:
|
||||||
|
* have a field named "time"
|
||||||
|
* have a frame meta dataTopic of "annotations"
|
||||||
|
* do not have a frame name of exemplar
|
||||||
|
* @param dataFrames
|
||||||
|
*/
|
||||||
export function getAnnotationFrames(dataFrames: DataFrame[] = []) {
|
export function getAnnotationFrames(dataFrames: DataFrame[] = []) {
|
||||||
return dataFrames.filter(
|
return dataFrames.filter(
|
||||||
(frame) => frame.name !== 'exemplar' && frame.length > 0 && frame.fields.some((f) => f.name === 'time')
|
(frame) =>
|
||||||
|
frame.name !== 'exemplar' &&
|
||||||
|
frame.length > 0 &&
|
||||||
|
frame.fields.some((f) => f.name === 'time' && frame.meta?.dataTopic === DataTopic.Annotations)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue