Sparkline: Improve min/max logic to avoid issues for very narrow deltas (#115030)

* Sparkline: Prevent infinite loop when rendering a sparkline with a single value

* some tests for this case

* refactor out utils, experiment with getting highlightIndex working

* add comments throughout for #112977

* remove unused import

* Update Sparkline.test.tsx

* fix points mode rendering

* Sparkline: Improve min/max logic to avoid issues for very narrow deltas

* spread all config

* defaults deep

* delete unused import

* remove go.work.sum delta

* line break at end of file
This commit is contained in:
Paul Marbach
2025-12-10 16:54:29 -05:00
committed by GitHub
parent e433cfa02d
commit 000c00aee9
4 changed files with 96 additions and 30 deletions
@@ -253,7 +253,7 @@ describe('applyFieldOverrides', () => {
],
});
it('will apply field overrides to the fields within the frame', () => {
it('will apply default field overrides to the fields within the frame', () => {
const f0 = createDataFrame({
name: 'A',
fields: [
@@ -285,6 +285,41 @@ describe('applyFieldOverrides', () => {
expect(withOverrides[0].fields[1].values[0].fields[1].state.range.max).toBe(30);
});
it('will apply targeted field overrides to the fields within the frame', () => {
const f0 = createDataFrame({
name: 'A',
fields: [
{
name: 'message',
type: FieldType.string,
values: ['foo'],
},
{
name: 'frame',
type: FieldType.frame,
values: [f0Internal],
},
],
});
const withOverrides = applyFieldOverrides({
data: [f0],
fieldConfig: {
defaults: {},
overrides: [
{
matcher: { id: FieldMatcherID.byName, options: 'frame' },
properties: [{ id: 'max', value: 30 }],
},
],
},
replaceVariables: (value) => value,
theme: createTheme(),
fieldConfigRegistry: customFieldRegistry,
});
expect(withOverrides[0].fields[1].values[0].fields[1].config.max).toBe(30);
});
it('will not crash when some of the nested frames are undefined', () => {
const f0 = createDataFrame({
name: 'A',
@@ -1,4 +1,4 @@
import { isNumber, set, unset, get, cloneDeep } from 'lodash';
import { isNumber, set, unset, get, cloneDeep, defaultsDeep } from 'lodash';
import { createContext, useContext, useMemo, useRef } from 'react';
import { usePrevious } from 'react-use';
@@ -240,9 +240,14 @@ export function applyFieldOverrides(options: ApplyFieldOverrideOptions): DataFra
...options,
// nested frames can be `undefined` in certain situations, like after `merge` transform due to padding the value array.
// let's replace them with empty frames to avoid errors applying overrides
data: field.values.map(
(nestedFrame: DataFrame | undefined): DataFrame => nestedFrame ?? createDataFrame({ fields: [] })
),
data: field.values.map((nestedFrame: DataFrame | undefined): DataFrame => {
const result = nestedFrame ?? createDataFrame({ fields: [] });
result.fields = result.fields.map((newField) => {
newField.config = defaultsDeep(newField.config || {}, config);
return newField;
});
return result;
}),
});
}
}
@@ -256,7 +261,7 @@ function calculateRange(
field: Field,
globalRange: NumericRange | undefined,
data: DataFrame[]
): { range?: { min?: number | null; max?: number | null; delta: number }; newGlobalRange: NumericRange | undefined } {
): { range?: NumericRange; newGlobalRange?: NumericRange } {
// If range is defined with min/max, use it
if (isNumber(config.min) && isNumber(config.max)) {
const range = { min: config.min, max: config.max, delta: config.max - config.min };