mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Timeseries: Fix null pointer when matching fill below to field (#58030)
* Fix timeseries fill below to error * wip * modifications * add tests for fillBelowTo option
This commit is contained in:
parent
bdb8fe3b02
commit
d673c8809b
275
packages/grafana-ui/src/components/TimeSeries/utils.test.ts
Normal file
275
packages/grafana-ui/src/components/TimeSeries/utils.test.ts
Normal file
@ -0,0 +1,275 @@
|
||||
import { ArrayVector, FieldType } from '@grafana/data';
|
||||
import { getTheme } from '@grafana/ui';
|
||||
|
||||
import { preparePlotConfigBuilder } from './utils';
|
||||
|
||||
describe('when fill below to option is used', () => {
|
||||
// eslint-disable-next-line
|
||||
let eventBus: any;
|
||||
// eslint-disable-next-line
|
||||
let renderers: any[];
|
||||
// eslint-disable-next-line
|
||||
let tests: any;
|
||||
|
||||
beforeEach(() => {
|
||||
eventBus = {
|
||||
publish: jest.fn(),
|
||||
getStream: jest.fn(),
|
||||
subscribe: jest.fn(),
|
||||
removeAllListeners: jest.fn(),
|
||||
newScopedBus: jest.fn(),
|
||||
};
|
||||
renderers = [];
|
||||
|
||||
tests = [
|
||||
{
|
||||
alignedFrame: {
|
||||
fields: [
|
||||
{
|
||||
config: {},
|
||||
values: new ArrayVector([1667406900000, 1667407170000, 1667407185000]),
|
||||
name: 'Time',
|
||||
state: { multipleFrames: true, displayName: 'Time', origin: { fieldIndex: 0, frameIndex: 0 } },
|
||||
type: FieldType.time,
|
||||
},
|
||||
{
|
||||
config: { displayNameFromDS: 'Test1', custom: { fillBelowTo: 'Test2' }, min: 0, max: 100 },
|
||||
values: new ArrayVector([1, 2, 3]),
|
||||
name: 'Value',
|
||||
state: { multipleFrames: true, displayName: 'Test1', origin: { fieldIndex: 1, frameIndex: 0 } },
|
||||
type: FieldType.number,
|
||||
},
|
||||
{
|
||||
config: { displayNameFromDS: 'Test2', min: 0, max: 100 },
|
||||
values: new ArrayVector([4, 5, 6]),
|
||||
name: 'Value',
|
||||
state: { multipleFrames: true, displayName: 'Test2', origin: { fieldIndex: 1, frameIndex: 1 } },
|
||||
type: FieldType.number,
|
||||
},
|
||||
],
|
||||
length: 3,
|
||||
},
|
||||
allFrames: [
|
||||
{
|
||||
name: 'Test1',
|
||||
refId: 'A',
|
||||
fields: [
|
||||
{
|
||||
config: {},
|
||||
values: new ArrayVector([1667406900000, 1667407170000, 1667407185000]),
|
||||
name: 'Time',
|
||||
state: { multipleFrames: true, displayName: 'Time', origin: { fieldIndex: 0, frameIndex: 0 } },
|
||||
type: FieldType.time,
|
||||
},
|
||||
{
|
||||
config: { displayNameFromDS: 'Test1', custom: { fillBelowTo: 'Test2' }, min: 0, max: 100 },
|
||||
values: new ArrayVector([1, 2, 3]),
|
||||
name: 'Value',
|
||||
state: { multipleFrames: true, displayName: 'Test1', origin: { fieldIndex: 1, frameIndex: 0 } },
|
||||
type: FieldType.number,
|
||||
},
|
||||
],
|
||||
length: 2,
|
||||
},
|
||||
{
|
||||
name: 'Test2',
|
||||
refId: 'B',
|
||||
fields: [
|
||||
{
|
||||
config: {},
|
||||
values: new ArrayVector([1667406900000, 1667407170000, 1667407185000]),
|
||||
name: 'Time',
|
||||
state: { multipleFrames: true, displayName: 'Time', origin: { fieldIndex: 0, frameIndex: 1 } },
|
||||
type: FieldType.time,
|
||||
},
|
||||
{
|
||||
config: { displayNameFromDS: 'Test2', min: 0, max: 100 },
|
||||
values: new ArrayVector([1, 2, 3]),
|
||||
name: 'Value',
|
||||
state: { multipleFrames: true, displayName: 'Test2', origin: { fieldIndex: 1, frameIndex: 1 } },
|
||||
type: FieldType.number,
|
||||
},
|
||||
],
|
||||
length: 2,
|
||||
},
|
||||
],
|
||||
expectedResult: 1,
|
||||
},
|
||||
{
|
||||
alignedFrame: {
|
||||
fields: [
|
||||
{
|
||||
config: {},
|
||||
values: new ArrayVector([1667406900000, 1667407170000, 1667407185000]),
|
||||
name: 'time',
|
||||
state: { multipleFrames: true, displayName: 'time', origin: { fieldIndex: 0, frameIndex: 0 } },
|
||||
type: FieldType.time,
|
||||
},
|
||||
{
|
||||
config: { custom: { fillBelowTo: 'below_value1' } },
|
||||
values: new ArrayVector([1, 2, 3]),
|
||||
name: 'value1',
|
||||
state: { multipleFrames: true, displayName: 'value1', origin: { fieldIndex: 1, frameIndex: 0 } },
|
||||
type: FieldType.number,
|
||||
},
|
||||
{
|
||||
config: { custom: { fillBelowTo: 'below_value2' } },
|
||||
values: new ArrayVector([4, 5, 6]),
|
||||
name: 'value2',
|
||||
state: { multipleFrames: true, displayName: 'value2', origin: { fieldIndex: 2, frameIndex: 0 } },
|
||||
type: FieldType.number,
|
||||
},
|
||||
{
|
||||
config: {},
|
||||
values: new ArrayVector([4, 5, 6]),
|
||||
name: 'below_value1',
|
||||
state: { multipleFrames: true, displayName: 'below_value1', origin: { fieldIndex: 1, frameIndex: 1 } },
|
||||
type: FieldType.number,
|
||||
},
|
||||
{
|
||||
config: {},
|
||||
values: new ArrayVector([4, 5, 6]),
|
||||
name: 'below_value2',
|
||||
state: { multipleFrames: true, displayName: 'below_value2', origin: { fieldIndex: 2, frameIndex: 1 } },
|
||||
type: FieldType.number,
|
||||
},
|
||||
],
|
||||
length: 5,
|
||||
},
|
||||
allFrames: [
|
||||
{
|
||||
refId: 'A',
|
||||
fields: [
|
||||
{
|
||||
config: {},
|
||||
values: new ArrayVector([1667406900000, 1667407170000, 1667407185000]),
|
||||
name: 'time',
|
||||
state: { multipleFrames: true, displayName: 'time', origin: { fieldIndex: 0, frameIndex: 0 } },
|
||||
type: FieldType.time,
|
||||
},
|
||||
{
|
||||
config: { custom: { fillBelowTo: 'below_value1' } },
|
||||
values: new ArrayVector([1, 2, 3]),
|
||||
name: 'value1',
|
||||
state: { multipleFrames: true, displayName: 'value1', origin: { fieldIndex: 1, frameIndex: 0 } },
|
||||
type: FieldType.number,
|
||||
},
|
||||
{
|
||||
config: { custom: { fillBelowTo: 'below_value2' } },
|
||||
values: new ArrayVector([4, 5, 6]),
|
||||
name: 'value2',
|
||||
state: { multipleFrames: true, displayName: 'value2', origin: { fieldIndex: 2, frameIndex: 0 } },
|
||||
type: FieldType.number,
|
||||
},
|
||||
],
|
||||
length: 3,
|
||||
},
|
||||
{
|
||||
refId: 'B',
|
||||
fields: [
|
||||
{
|
||||
config: {},
|
||||
values: new ArrayVector([1667406900000, 1667407170000, 1667407185000]),
|
||||
name: 'time',
|
||||
state: { multipleFrames: true, displayName: 'time', origin: { fieldIndex: 0, frameIndex: 1 } },
|
||||
type: FieldType.time,
|
||||
},
|
||||
{
|
||||
config: {},
|
||||
values: new ArrayVector([4, 5, 6]),
|
||||
name: 'below_value1',
|
||||
state: { multipleFrames: true, displayName: 'below_value1', origin: { fieldIndex: 1, frameIndex: 1 } },
|
||||
type: FieldType.number,
|
||||
},
|
||||
{
|
||||
config: {},
|
||||
values: new ArrayVector([4, 5, 6]),
|
||||
name: 'below_value2',
|
||||
state: { multipleFrames: true, displayName: 'below_value2', origin: { fieldIndex: 2, frameIndex: 1 } },
|
||||
type: FieldType.number,
|
||||
},
|
||||
],
|
||||
length: 3,
|
||||
},
|
||||
],
|
||||
expectedResult: 2,
|
||||
},
|
||||
];
|
||||
});
|
||||
|
||||
it('should verify if fill below to is set then builder bands are set', () => {
|
||||
for (const test of tests) {
|
||||
const builder = preparePlotConfigBuilder({
|
||||
frame: test.alignedFrame,
|
||||
//@ts-ignore
|
||||
theme: getTheme(),
|
||||
timeZones: ['browser'],
|
||||
getTimeRange: jest.fn(),
|
||||
eventBus,
|
||||
sync: jest.fn(),
|
||||
allFrames: test.allFrames,
|
||||
renderers,
|
||||
});
|
||||
|
||||
//@ts-ignore
|
||||
expect(builder.bands.length).toBe(test.expectedResult);
|
||||
}
|
||||
});
|
||||
|
||||
it('should verify if fill below to is not set then builder bands are empty', () => {
|
||||
tests[0].alignedFrame.fields[1].config.custom.fillBelowTo = undefined;
|
||||
tests[0].allFrames[0].fields[1].config.custom.fillBelowTo = undefined;
|
||||
tests[1].alignedFrame.fields[1].config.custom.fillBelowTo = undefined;
|
||||
tests[1].alignedFrame.fields[2].config.custom.fillBelowTo = undefined;
|
||||
tests[1].allFrames[0].fields[1].config.custom.fillBelowTo = undefined;
|
||||
tests[1].allFrames[0].fields[2].config.custom.fillBelowTo = undefined;
|
||||
tests[0].expectedResult = 0;
|
||||
tests[1].expectedResult = 0;
|
||||
|
||||
for (const test of tests) {
|
||||
const builder = preparePlotConfigBuilder({
|
||||
frame: test.alignedFrame,
|
||||
//@ts-ignore
|
||||
theme: getTheme(),
|
||||
timeZones: ['browser'],
|
||||
getTimeRange: jest.fn(),
|
||||
eventBus,
|
||||
sync: jest.fn(),
|
||||
allFrames: test.allFrames,
|
||||
renderers,
|
||||
});
|
||||
|
||||
//@ts-ignore
|
||||
expect(builder.bands.length).toBe(test.expectedResult);
|
||||
}
|
||||
});
|
||||
|
||||
it('should verify if fill below to is set and field name is overriden then builder bands are set', () => {
|
||||
tests[0].alignedFrame.fields[2].config.displayName = 'newName';
|
||||
tests[0].alignedFrame.fields[2].state.displayName = 'newName';
|
||||
tests[0].allFrames[1].fields[1].config.displayName = 'newName';
|
||||
tests[0].allFrames[1].fields[1].state.displayName = 'newName';
|
||||
|
||||
tests[1].alignedFrame.fields[3].config.displayName = 'newName';
|
||||
tests[1].alignedFrame.fields[3].state.displayName = 'newName';
|
||||
tests[1].allFrames[1].fields[1].config.displayName = 'newName';
|
||||
tests[1].allFrames[1].fields[1].state.displayName = 'newName';
|
||||
|
||||
for (const test of tests) {
|
||||
const builder = preparePlotConfigBuilder({
|
||||
frame: test.alignedFrame,
|
||||
//@ts-ignore
|
||||
theme: getTheme(),
|
||||
timeZones: ['browser'],
|
||||
getTimeRange: jest.fn(),
|
||||
eventBus,
|
||||
sync: jest.fn(),
|
||||
allFrames: test.allFrames,
|
||||
renderers,
|
||||
});
|
||||
|
||||
//@ts-ignore
|
||||
expect(builder.bands.length).toBe(test.expectedResult);
|
||||
}
|
||||
});
|
||||
});
|
@ -400,8 +400,16 @@ export const preparePlotConfigBuilder: UPlotConfigPrepFn<{
|
||||
}
|
||||
|
||||
if (customConfig.fillBelowTo) {
|
||||
const fillBelowToField = frame.fields.find((f) => f.name === customConfig.fillBelowTo);
|
||||
const fillBelowDispName = getFieldDisplayName(fillBelowToField!, frame, allFrames);
|
||||
const fillBelowToField = frame.fields.find(
|
||||
(f) =>
|
||||
customConfig.fillBelowTo === f.name ||
|
||||
customConfig.fillBelowTo === f.config?.displayNameFromDS ||
|
||||
customConfig.fillBelowTo === getFieldDisplayName(f, frame, allFrames)
|
||||
);
|
||||
|
||||
const fillBelowDispName = fillBelowToField
|
||||
? getFieldDisplayName(fillBelowToField, frame, allFrames)
|
||||
: customConfig.fillBelowTo;
|
||||
|
||||
const t = indexByName.get(dispName);
|
||||
const b = indexByName.get(fillBelowDispName);
|
||||
|
Loading…
Reference in New Issue
Block a user