mirror of https://github.com/grafana/grafana
TimeSeries/BarChart: Add support for sorting series in the tooltip (#43615)
* TimeSeries panel: Add support for sorting series in the tooltip * Fix cue tests * Make sortValues work with string values * Sort values in DatHoverView and remove sort index from TooltipPlugin * Rename sortOrder prop to sort * DataHoverView - use raw values for sortingpull/43858/head
parent
4b814dbcea
commit
1a0f5595c3
@ -0,0 +1,29 @@ |
||||
import { SortOrder } from '@grafana/schema'; |
||||
import { sortValues } from './arrayUtils'; |
||||
|
||||
describe('arrayUtils', () => { |
||||
describe('sortValues', () => { |
||||
const testArrayNumeric = [1, 10, null, 2, undefined, 5, 6, ' ', 7, 9, 8, 3, 4]; |
||||
const testArrayString = ['1', '10', null, '2', undefined, '5', '6', ' ', '7', '9', '8', '3', '4']; |
||||
const testArrayFloatingPoint = ['1.0', '2.7', '0.5', ' ', null, '2', undefined]; |
||||
const testArrayText = ['baz', ' ', 'foo', null, 'bar', undefined]; |
||||
const testArrayMixed = ['baz', ' ', 1, 'foo', '1.5', '0.5', null, 'bar', undefined]; |
||||
|
||||
it.each` |
||||
order | testArray | expected |
||||
${SortOrder.Ascending} | ${testArrayNumeric} | ${[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, ' ', null, undefined]} |
||||
${SortOrder.Descending} | ${testArrayNumeric} | ${[10, 9, 8, 7, 6, 5, 4, 3, 2, 1, ' ', null, undefined]} |
||||
${SortOrder.Ascending} | ${testArrayString} | ${['1', '2', '3', '4', '5', '6', '7', '8', '9', '10', ' ', null, undefined]} |
||||
${SortOrder.Descending} | ${testArrayString} | ${['10', '9', '8', '7', '6', '5', '4', '3', '2', '1', ' ', null, undefined]} |
||||
${SortOrder.Ascending} | ${testArrayFloatingPoint} | ${['0.5', '1.0', '2', '2.7', null, ' ', undefined]} |
||||
${SortOrder.Descending} | ${testArrayFloatingPoint} | ${['2.7', '2', '1.0', '0.5', null, ' ', undefined]} |
||||
${SortOrder.Ascending} | ${testArrayText} | ${['bar', 'baz', 'foo', null, ' ', undefined]} |
||||
${SortOrder.Descending} | ${testArrayText} | ${['foo', 'baz', 'bar', null, ' ', undefined]} |
||||
${SortOrder.Ascending} | ${testArrayMixed} | ${['0.5', 1, '1.5', 'bar', 'baz', 'foo', null, ' ', undefined]} |
||||
${SortOrder.Descending} | ${testArrayMixed} | ${['foo', 'baz', 'bar', '1.5', 1, '0.5', null, ' ', undefined]} |
||||
`('$order', ({ order, testArray, expected }) => {
|
||||
const sorted = [...testArray].sort(sortValues(order)); |
||||
expect(sorted).toEqual(expected); |
||||
}); |
||||
}); |
||||
}); |
@ -1,6 +1,33 @@ |
||||
import { SortOrder } from '@grafana/schema'; |
||||
|
||||
/** @internal */ |
||||
export function moveItemImmutably<T>(arr: T[], from: number, to: number) { |
||||
const clone = [...arr]; |
||||
Array.prototype.splice.call(clone, to, 0, Array.prototype.splice.call(clone, from, 1)[0]); |
||||
return clone; |
||||
} |
||||
|
||||
/** |
||||
* Given a sort order and a value, return a function that can be used to sort values |
||||
* Null/undefined/empty string values are always sorted to the end regardless of the sort order provided |
||||
*/ |
||||
const collator = new Intl.Collator(undefined, { numeric: true, sensitivity: 'base' }); |
||||
export function sortValues(sort: SortOrder.Ascending | SortOrder.Descending) { |
||||
return (a: any, b: any) => { |
||||
if (a === b) { |
||||
return 0; |
||||
} |
||||
|
||||
if (b == null || (typeof b === 'string' && b.trim() === '')) { |
||||
return -1; |
||||
} |
||||
if (a == null || (typeof a === 'string' && a?.trim() === '')) { |
||||
return 1; |
||||
} |
||||
|
||||
if (sort === SortOrder.Descending) { |
||||
return collator.compare(b, a); |
||||
} |
||||
return collator.compare(a, b); |
||||
}; |
||||
} |
||||
|
@ -1,7 +1,9 @@ |
||||
package schema |
||||
|
||||
TooltipDisplayMode: "single" | "multi" | "none" @cuetsy(kind="enum") |
||||
SortOrder: "asc" | "desc" | "none" @cuetsy(kind="enum") |
||||
|
||||
VizTooltipOptions: { |
||||
mode: TooltipDisplayMode |
||||
sort: SortOrder |
||||
} @cuetsy(kind="interface") |
||||
|
@ -1,29 +1,46 @@ |
||||
import { OptionsWithTooltip } from '@grafana/schema'; |
||||
import { OptionsWithTooltip, TooltipDisplayMode, SortOrder } from '@grafana/schema'; |
||||
import { PanelOptionsEditorBuilder } from '@grafana/data'; |
||||
|
||||
export function addTooltipOptions<T extends OptionsWithTooltip>( |
||||
builder: PanelOptionsEditorBuilder<T>, |
||||
singleOnly = false |
||||
) { |
||||
const options = singleOnly |
||||
const category = ['Tooltip']; |
||||
const modeOptions = singleOnly |
||||
? [ |
||||
{ value: 'single', label: 'Single' }, |
||||
{ value: 'none', label: 'Hidden' }, |
||||
{ value: TooltipDisplayMode.Single, label: 'Single' }, |
||||
{ value: TooltipDisplayMode.None, label: 'Hidden' }, |
||||
] |
||||
: [ |
||||
{ value: 'single', label: 'Single' }, |
||||
{ value: 'multi', label: 'All' }, |
||||
{ value: 'none', label: 'Hidden' }, |
||||
{ value: TooltipDisplayMode.Single, label: 'Single' }, |
||||
{ value: TooltipDisplayMode.Multi, label: 'All' }, |
||||
{ value: TooltipDisplayMode.None, label: 'Hidden' }, |
||||
]; |
||||
|
||||
builder.addRadio({ |
||||
path: 'tooltip.mode', |
||||
name: 'Tooltip mode', |
||||
category: ['Tooltip'], |
||||
description: '', |
||||
defaultValue: 'single', |
||||
settings: { |
||||
options, |
||||
}, |
||||
}); |
||||
const sortOptions = [ |
||||
{ value: SortOrder.None, label: 'None' }, |
||||
{ value: SortOrder.Ascending, label: 'Ascending' }, |
||||
{ value: SortOrder.Descending, label: 'Descending' }, |
||||
]; |
||||
|
||||
builder |
||||
.addRadio({ |
||||
path: 'tooltip.mode', |
||||
name: 'Tooltip mode', |
||||
category, |
||||
defaultValue: 'single', |
||||
settings: { |
||||
options: modeOptions, |
||||
}, |
||||
}) |
||||
.addRadio({ |
||||
path: 'tooltip.sort', |
||||
name: 'Values sort order', |
||||
category, |
||||
defaultValue: SortOrder.None, |
||||
showIf: (options: T) => options.tooltip.mode === TooltipDisplayMode.Multi, |
||||
settings: { |
||||
options: sortOptions, |
||||
}, |
||||
}); |
||||
} |
||||
|
Loading…
Reference in new issue