diff --git a/devenv/dev-dashboards/all-panels.json b/devenv/dev-dashboards/all-panels.json index 1be537a5cfe..ddd2dbe4ea0 100644 --- a/devenv/dev-dashboards/all-panels.json +++ b/devenv/dev-dashboards/all-panels.json @@ -161,7 +161,8 @@ "rowHeight": 0.99, "showValue": "auto", "tooltip": { - "mode": "single" + "mode": "single", + "sort": "none" } }, "targets": [ @@ -545,7 +546,8 @@ "values": false }, "tooltip": { - "mode": "single" + "mode": "single", + "sort": "none" } }, "targets": [ @@ -792,7 +794,8 @@ "placement": "bottom" }, "tooltip": { - "mode": "single" + "mode": "single", + "sort": "none" } }, "targets": [ @@ -861,7 +864,8 @@ "stacking": "none", "text": {}, "tooltip": { - "mode": "single" + "mode": "single", + "sort": "none" } }, "targets": [ diff --git a/devenv/dev-dashboards/datasource-elasticsearch/elasticsearch_compare.json b/devenv/dev-dashboards/datasource-elasticsearch/elasticsearch_compare.json index f0da50403d6..061d93f9fb0 100644 --- a/devenv/dev-dashboards/datasource-elasticsearch/elasticsearch_compare.json +++ b/devenv/dev-dashboards/datasource-elasticsearch/elasticsearch_compare.json @@ -10535,7 +10535,8 @@ "placement": "bottom" }, "tooltip": { - "mode": "single" + "mode": "single", + "sort": "none" } }, "targets": [ @@ -10663,7 +10664,8 @@ "placement": "bottom" }, "tooltip": { - "mode": "single" + "mode": "single", + "sort": "none" } }, "targets": [ diff --git a/devenv/dev-dashboards/datasource-testdata/new_features_in_v8.json b/devenv/dev-dashboards/datasource-testdata/new_features_in_v8.json index 450e3a8c622..82b6c8904a7 100644 --- a/devenv/dev-dashboards/datasource-testdata/new_features_in_v8.json +++ b/devenv/dev-dashboards/datasource-testdata/new_features_in_v8.json @@ -319,7 +319,8 @@ "placement": "bottom" }, "tooltip": { - "mode": "single" + "mode": "single", + "sort": "none" } }, "pluginVersion": "8.0.0-beta2", @@ -605,7 +606,8 @@ "showValue": "auto", "text": {}, "tooltip": { - "mode": "single" + "mode": "single", + "sort": "none" } }, "targets": [ @@ -726,7 +728,8 @@ "showValue": "auto", "text": {}, "tooltip": { - "mode": "single" + "mode": "single", + "sort": "none" } }, "targets": [ @@ -872,7 +875,8 @@ "placement": "bottom" }, "tooltip": { - "mode": "single" + "mode": "single", + "sort": "none" } }, "pluginVersion": "7.4.0-beta1", @@ -990,7 +994,8 @@ "placement": "bottom" }, "tooltip": { - "mode": "single" + "mode": "single", + "sort": "none" } }, "pluginVersion": "7.4.0-beta1", @@ -1108,7 +1113,8 @@ "placement": "bottom" }, "tooltip": { - "mode": "single" + "mode": "single", + "sort": "none" } }, "pluginVersion": "7.4.0-beta1", @@ -1235,7 +1241,8 @@ "placement": "bottom" }, "tooltip": { - "mode": "single" + "mode": "single", + "sort": "none" } }, "pluginVersion": "7.4.0-beta1", @@ -1339,7 +1346,8 @@ "placement": "bottom" }, "tooltip": { - "mode": "single" + "mode": "single", + "sort": "none" } }, "pluginVersion": "7.4.0-beta1", @@ -1443,7 +1451,8 @@ "placement": "bottom" }, "tooltip": { - "mode": "single" + "mode": "single", + "sort": "none" } }, "pluginVersion": "7.4.0-beta1", @@ -1623,7 +1632,8 @@ "placement": "bottom" }, "tooltip": { - "mode": "multi" + "mode": "multi", + "sort": "none" } }, "pluginVersion": "7.4.0-beta1", @@ -1899,7 +1909,8 @@ "placement": "bottom" }, "tooltip": { - "mode": "single" + "mode": "single", + "sort": "none" } }, "pluginVersion": "7.4.0-beta1", @@ -2221,7 +2232,8 @@ "placement": "bottom" }, "tooltip": { - "mode": "single" + "mode": "single", + "sort": "none" } }, "pluginVersion": "7.4.0-beta1", @@ -2475,7 +2487,8 @@ "placement": "bottom" }, "tooltip": { - "mode": "single" + "mode": "single", + "sort": "none" } }, "pluginVersion": "7.4.0-beta1", diff --git a/devenv/dev-dashboards/panel-barchart/barchart-autosizing.json b/devenv/dev-dashboards/panel-barchart/barchart-autosizing.json index b14ed0bd244..db655f110e3 100644 --- a/devenv/dev-dashboards/panel-barchart/barchart-autosizing.json +++ b/devenv/dev-dashboards/panel-barchart/barchart-autosizing.json @@ -79,7 +79,8 @@ "stacking": "none", "text": {}, "tooltip": { - "mode": "single" + "mode": "single", + "sort": "none" } }, "targets": [ @@ -151,7 +152,8 @@ "stacking": "none", "text": {}, "tooltip": { - "mode": "single" + "mode": "single", + "sort": "none" } }, "targets": [ @@ -221,7 +223,8 @@ "stacking": "none", "text": {}, "tooltip": { - "mode": "single" + "mode": "single", + "sort": "none" } }, "targets": [ @@ -291,7 +294,8 @@ "stacking": "none", "text": {}, "tooltip": { - "mode": "single" + "mode": "single", + "sort": "none" } }, "targets": [ @@ -363,7 +367,8 @@ "valueSize": 25 }, "tooltip": { - "mode": "single" + "mode": "single", + "sort": "none" } }, "targets": [ @@ -433,7 +438,8 @@ "stacking": "none", "text": {}, "tooltip": { - "mode": "single" + "mode": "single", + "sort": "none" } }, "targets": [ @@ -504,7 +510,8 @@ "stacking": "none", "text": {}, "tooltip": { - "mode": "single" + "mode": "single", + "sort": "none" } }, "targets": [ diff --git a/devenv/dev-dashboards/panel-graph/graph-ng-by-value-color-schemes.json b/devenv/dev-dashboards/panel-graph/graph-ng-by-value-color-schemes.json index 8139a650d44..ee567e26d2b 100644 --- a/devenv/dev-dashboards/panel-graph/graph-ng-by-value-color-schemes.json +++ b/devenv/dev-dashboards/panel-graph/graph-ng-by-value-color-schemes.json @@ -95,7 +95,8 @@ "placement": "bottom" }, "tooltip": { - "mode": "single" + "mode": "single", + "sort": "none" } }, "targets": [ @@ -181,7 +182,8 @@ "placement": "bottom" }, "tooltip": { - "mode": "single" + "mode": "single", + "sort": "none" } }, "targets": [ @@ -266,7 +268,8 @@ "placement": "bottom" }, "tooltip": { - "mode": "single" + "mode": "single", + "sort": "none" } }, "targets": [ @@ -352,7 +355,8 @@ "placement": "bottom" }, "tooltip": { - "mode": "single" + "mode": "single", + "sort": "none" } }, "targets": [ @@ -437,7 +441,8 @@ "placement": "bottom" }, "tooltip": { - "mode": "single" + "mode": "single", + "sort": "none" } }, "targets": [ @@ -531,7 +536,8 @@ "placement": "bottom" }, "tooltip": { - "mode": "single" + "mode": "single", + "sort": "none" } }, "targets": [ @@ -620,7 +626,8 @@ "placement": "bottom" }, "tooltip": { - "mode": "single" + "mode": "single", + "sort": "none" } }, "targets": [ @@ -709,7 +716,8 @@ "placement": "bottom" }, "tooltip": { - "mode": "single" + "mode": "single", + "sort": "none" } }, "targets": [ @@ -797,7 +805,8 @@ "placement": "bottom" }, "tooltip": { - "mode": "single" + "mode": "single", + "sort": "none" } }, "targets": [ diff --git a/devenv/dev-dashboards/panel-graph/graph-ng-nulls.json b/devenv/dev-dashboards/panel-graph/graph-ng-nulls.json index fa2c31200ac..d927719fd27 100644 --- a/devenv/dev-dashboards/panel-graph/graph-ng-nulls.json +++ b/devenv/dev-dashboards/panel-graph/graph-ng-nulls.json @@ -100,7 +100,8 @@ "placement": "bottom" }, "tooltip": { - "mode": "single" + "mode": "single", + "sort": "none" } }, "pluginVersion": "7.4.0-pre", @@ -206,7 +207,8 @@ "placement": "bottom" }, "tooltip": { - "mode": "single" + "mode": "single", + "sort": "none" } }, "pluginVersion": "7.4.0-pre", @@ -320,7 +322,8 @@ "placement": "bottom" }, "tooltip": { - "mode": "single" + "mode": "single", + "sort": "none" } }, "pluginVersion": "7.4.0-pre", @@ -452,7 +455,8 @@ "placement": "bottom" }, "tooltip": { - "mode": "single" + "mode": "single", + "sort": "none" } }, "pluginVersion": "7.4.0-pre", @@ -558,7 +562,8 @@ "placement": "bottom" }, "tooltip": { - "mode": "single" + "mode": "single", + "sort": "none" } }, "pluginVersion": "7.4.0-pre", @@ -672,7 +677,8 @@ "placement": "bottom" }, "tooltip": { - "mode": "single" + "mode": "single", + "sort": "none" } }, "pluginVersion": "7.4.0-pre", @@ -836,7 +842,8 @@ "placement": "bottom" }, "tooltip": { - "mode": "single" + "mode": "single", + "sort": "none" } }, "pluginVersion": "7.2.0-pre", @@ -976,7 +983,8 @@ "placement": "bottom" }, "tooltip": { - "mode": "single" + "mode": "single", + "sort": "none" } }, "pluginVersion": "7.2.0-pre", @@ -1082,7 +1090,8 @@ "placement": "bottom" }, "tooltip": { - "mode": "single" + "mode": "single", + "sort": "none" } }, "pluginVersion": "7.5.0-pre", @@ -1184,7 +1193,8 @@ "placement": "bottom" }, "tooltip": { - "mode": "single" + "mode": "single", + "sort": "none" } }, "targets": [ diff --git a/devenv/dev-dashboards/panel-timeline/timeline-demo.json b/devenv/dev-dashboards/panel-timeline/timeline-demo.json index d0fc5ecb727..2c642371bf5 100644 --- a/devenv/dev-dashboards/panel-timeline/timeline-demo.json +++ b/devenv/dev-dashboards/panel-timeline/timeline-demo.json @@ -89,7 +89,8 @@ "rowHeight": 0.98, "showValue": "always", "tooltip": { - "mode": "single" + "mode": "single", + "sort": "none" } }, "pluginVersion": "7.5.0-pre", @@ -186,7 +187,8 @@ "rowHeight": 0.98, "showValue": "always", "tooltip": { - "mode": "single" + "mode": "single", + "sort": "none" } }, "targets": [ @@ -287,7 +289,8 @@ "rowHeight": 0.98, "showValue": "always", "tooltip": { - "mode": "single" + "mode": "single", + "sort": "none" } }, "targets": [ @@ -367,7 +370,8 @@ "rowHeight": 0.98, "showValue": "always", "tooltip": { - "mode": "single" + "mode": "single", + "sort": "asc" } }, "pluginVersion": "7.5.0-pre", diff --git a/devenv/dev-dashboards/panel-timeline/timeline-modes.json b/devenv/dev-dashboards/panel-timeline/timeline-modes.json index 20faf6452a7..22256080fd7 100644 --- a/devenv/dev-dashboards/panel-timeline/timeline-modes.json +++ b/devenv/dev-dashboards/panel-timeline/timeline-modes.json @@ -68,7 +68,8 @@ "rowHeight": 0.9, "showValue": "always", "tooltip": { - "mode": "single" + "mode": "single", + "sort": "none" } }, "pluginVersion": "7.5.0-pre", @@ -244,7 +245,8 @@ "rowHeight": 0.9, "showValue": "always", "tooltip": { - "mode": "single" + "mode": "single", + "sort": "none" } }, "pluginVersion": "7.5.0-pre", @@ -319,7 +321,8 @@ "rowHeight": 0.9, "showValue": "always", "tooltip": { - "mode": "single" + "mode": "single", + "sort": "none" } }, "pluginVersion": "7.5.0-pre", diff --git a/devenv/dev-dashboards/transforms/config-from-query.json b/devenv/dev-dashboards/transforms/config-from-query.json index af802508422..9b72d9f1025 100644 --- a/devenv/dev-dashboards/transforms/config-from-query.json +++ b/devenv/dev-dashboards/transforms/config-from-query.json @@ -83,7 +83,8 @@ "placement": "bottom" }, "tooltip": { - "mode": "single" + "mode": "single", + "sort": "none" } }, "targets": [ @@ -484,7 +485,8 @@ "showValue": "auto", "text": {}, "tooltip": { - "mode": "single" + "mode": "single", + "sort": "none" } }, "pluginVersion": "8.1.0-pre", diff --git a/packages/grafana-data/src/utils/arrayUtils.test.ts b/packages/grafana-data/src/utils/arrayUtils.test.ts new file mode 100644 index 00000000000..6f7c81aa8ea --- /dev/null +++ b/packages/grafana-data/src/utils/arrayUtils.test.ts @@ -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); + }); + }); +}); diff --git a/packages/grafana-data/src/utils/arrayUtils.ts b/packages/grafana-data/src/utils/arrayUtils.ts index 2ad7e7dda07..e8f4ab405d3 100644 --- a/packages/grafana-data/src/utils/arrayUtils.ts +++ b/packages/grafana-data/src/utils/arrayUtils.ts @@ -1,6 +1,33 @@ +import { SortOrder } from '@grafana/schema'; + /** @internal */ export function moveItemImmutably(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); + }; +} diff --git a/packages/grafana-schema/src/schema/graph.gen.ts b/packages/grafana-schema/src/schema/graph.gen.ts index c6c8e20ac17..0ed0f810bf4 100644 --- a/packages/grafana-schema/src/schema/graph.gen.ts +++ b/packages/grafana-schema/src/schema/graph.gen.ts @@ -239,6 +239,12 @@ export enum TooltipDisplayMode { Single = 'single', } +export enum SortOrder { + Ascending = 'asc', + Descending = 'desc', + None = 'none', +} + export interface GraphFieldConfig extends LineConfig, FillConfig, @@ -284,4 +290,5 @@ export const defaultTableFieldOptions: TableFieldOptions = { export interface VizTooltipOptions { mode: TooltipDisplayMode; + sort: SortOrder; } diff --git a/packages/grafana-schema/src/schema/tooltip.cue b/packages/grafana-schema/src/schema/tooltip.cue index c58ee7394e3..464ba8ef511 100644 --- a/packages/grafana-schema/src/schema/tooltip.cue +++ b/packages/grafana-schema/src/schema/tooltip.cue @@ -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") diff --git a/packages/grafana-ui/src/components/uPlot/plugins/TooltipPlugin.tsx b/packages/grafana-ui/src/components/uPlot/plugins/TooltipPlugin.tsx index 4a8fb96eba3..75ebb4e8d8f 100644 --- a/packages/grafana-ui/src/components/uPlot/plugins/TooltipPlugin.tsx +++ b/packages/grafana-ui/src/components/uPlot/plugins/TooltipPlugin.tsx @@ -2,6 +2,7 @@ import React, { useEffect, useLayoutEffect, useRef, useState } from 'react'; import { useMountedState } from 'react-use'; import uPlot from 'uplot'; import { + arrayUtils, CartesianCoords2D, DashboardCursorSync, DataFrame, @@ -12,7 +13,7 @@ import { getFieldDisplayName, TimeZone, } from '@grafana/data'; -import { TooltipDisplayMode } from '@grafana/schema'; +import { TooltipDisplayMode, SortOrder } from '@grafana/schema'; import { useTheme2 } from '../../../themes/ThemeContext'; import { Portal } from '../../Portal/Portal'; import { SeriesTable, SeriesTableRowProps, VizTooltipContainer } from '../../VizTooltip'; @@ -24,6 +25,7 @@ interface TooltipPluginProps { data: DataFrame; config: UPlotConfigBuilder; mode?: TooltipDisplayMode; + sortOrder?: SortOrder; sync?: DashboardCursorSync; // Allows custom tooltip content rendering. Exposes aligned data frame with relevant indexes for data inspection // Use field.state.origin indexes from alignedData frame field to get access to original data frame and field index. @@ -37,6 +39,7 @@ const TOOLTIP_OFFSET = 10; */ export const TooltipPlugin: React.FC = ({ mode = TooltipDisplayMode.Single, + sortOrder = SortOrder.None, sync, timeZone, config, @@ -206,6 +209,7 @@ export const TooltipPlugin: React.FC = ({ let series: SeriesTableRowProps[] = []; const frame = otherProps.data; const fields = frame.fields; + const sortIdx: Array<[number, number]> = []; for (let i = 0; i < fields.length; i++) { const field = frame.fields[i]; @@ -220,8 +224,9 @@ export const TooltipPlugin: React.FC = ({ continue; } - const display = field.display!(otherProps.data.fields[i].values.get(focusedPointIdxs[i]!)); - + const v = otherProps.data.fields[i].values.get(focusedPointIdxs[i]!); + const display = field.display!(v); + sortIdx.push([series.length, v]); series.push({ color: display.color || FALLBACK_COLOR, label: getFieldDisplayName(field, frame), @@ -230,6 +235,10 @@ export const TooltipPlugin: React.FC = ({ }); } + if (sortOrder !== SortOrder.None) { + series.sort((a, b) => arrayUtils.sortValues(sortOrder)(a.value, b.value)); + } + tooltip = ; } } else { diff --git a/packages/grafana-ui/src/options/builder/tooltip.tsx b/packages/grafana-ui/src/options/builder/tooltip.tsx index db8554e1b34..3136c77ef2f 100644 --- a/packages/grafana-ui/src/options/builder/tooltip.tsx +++ b/packages/grafana-ui/src/options/builder/tooltip.tsx @@ -1,29 +1,46 @@ -import { OptionsWithTooltip } from '@grafana/schema'; +import { OptionsWithTooltip, TooltipDisplayMode, SortOrder } from '@grafana/schema'; import { PanelOptionsEditorBuilder } from '@grafana/data'; export function addTooltipOptions( builder: PanelOptionsEditorBuilder, 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, + }, + }); } diff --git a/pkg/schema/testdata/devenvgoldenfiles/all-panels.json b/pkg/schema/testdata/devenvgoldenfiles/all-panels.json index 9f1e4565489..8365f6a1628 100644 --- a/pkg/schema/testdata/devenvgoldenfiles/all-panels.json +++ b/pkg/schema/testdata/devenvgoldenfiles/all-panels.json @@ -189,7 +189,8 @@ "rowHeight": 0.99, "showValue": "auto", "tooltip": { - "mode": "single" + "mode": "single", + "sort": "none" } }, "targets": [ @@ -388,7 +389,7 @@ "showCommonLabels": false, "showLabels": false, "showTime": false, - "sortOrder": "Descending", + "sort": "Descending", "wrapLogMessage": false }, "targets": [ @@ -477,7 +478,7 @@ "dashboardAlerts": false, "maxItems": 20, "showInstances": false, - "sortOrder": 1, + "sort": 1, "stateFilter": { "firing": true, "inactive": false, @@ -651,7 +652,8 @@ "values": false }, "tooltip": { - "mode": "single" + "mode": "single", + "sort": "none" } }, "targets": [ @@ -937,7 +939,8 @@ "isVisible": false }, "tooltip": { - "mode": "single" + "mode": "single", + "sort": "none" } }, "targets": [ @@ -1015,7 +1018,8 @@ "stacking": "none", "text": {}, "tooltip": { - "mode": "single" + "mode": "single", + "sort": "none" } }, "targets": [ diff --git a/pkg/schema/testdata/devenvgoldenfiles/barchart-autosizing.json b/pkg/schema/testdata/devenvgoldenfiles/barchart-autosizing.json index fca76334feb..1df2163d51e 100644 --- a/pkg/schema/testdata/devenvgoldenfiles/barchart-autosizing.json +++ b/pkg/schema/testdata/devenvgoldenfiles/barchart-autosizing.json @@ -82,7 +82,8 @@ "stacking": "none", "text": {}, "tooltip": { - "mode": "single" + "mode": "single", + "sort": "none" } }, "targets": [ @@ -163,7 +164,8 @@ "stacking": "none", "text": {}, "tooltip": { - "mode": "single" + "mode": "single", + "sort": "none" } }, "targets": [ @@ -242,7 +244,8 @@ "stacking": "none", "text": {}, "tooltip": { - "mode": "single" + "mode": "single", + "sort": "none" } }, "targets": [ @@ -321,7 +324,8 @@ "stacking": "none", "text": {}, "tooltip": { - "mode": "single" + "mode": "single", + "sort": "none" } }, "targets": [ @@ -402,7 +406,8 @@ "valueSize": 25 }, "tooltip": { - "mode": "single" + "mode": "single", + "sort": "none" } }, "targets": [ @@ -481,7 +486,8 @@ "stacking": "none", "text": {}, "tooltip": { - "mode": "single" + "mode": "single", + "sort": "none" } }, "targets": [ @@ -561,7 +567,8 @@ "stacking": "none", "text": {}, "tooltip": { - "mode": "single" + "mode": "single", + "sort": "none" } }, "targets": [ diff --git a/pkg/schema/testdata/devenvgoldenfiles/config-from-query.json b/pkg/schema/testdata/devenvgoldenfiles/config-from-query.json index d2f0fcf0a4d..71c0e5056f3 100644 --- a/pkg/schema/testdata/devenvgoldenfiles/config-from-query.json +++ b/pkg/schema/testdata/devenvgoldenfiles/config-from-query.json @@ -86,7 +86,8 @@ "isVisible": false }, "tooltip": { - "mode": "single" + "mode": "single", + "sort": "none" } }, "targets": [ @@ -528,7 +529,8 @@ "showValue": "auto", "text": {}, "tooltip": { - "mode": "single" + "mode": "single", + "sort": "none" } }, "pluginVersion": "8.1.0-pre", diff --git a/pkg/schema/testdata/devenvgoldenfiles/elasticsearch_compare.json b/pkg/schema/testdata/devenvgoldenfiles/elasticsearch_compare.json index 18c2ad46e4f..45a25564fba 100644 --- a/pkg/schema/testdata/devenvgoldenfiles/elasticsearch_compare.json +++ b/pkg/schema/testdata/devenvgoldenfiles/elasticsearch_compare.json @@ -10533,7 +10533,8 @@ "isVisible": false }, "tooltip": { - "mode": "single" + "mode": "single", + "sort": "none" } }, "targets": [ @@ -10670,7 +10671,8 @@ "isVisible": false }, "tooltip": { - "mode": "single" + "mode": "single", + "sort": "none" } }, "targets": [ diff --git a/pkg/schema/testdata/devenvgoldenfiles/graph-ng-by-value-color-schemes.json b/pkg/schema/testdata/devenvgoldenfiles/graph-ng-by-value-color-schemes.json index 69c39b25448..8a12de95330 100644 --- a/pkg/schema/testdata/devenvgoldenfiles/graph-ng-by-value-color-schemes.json +++ b/pkg/schema/testdata/devenvgoldenfiles/graph-ng-by-value-color-schemes.json @@ -98,7 +98,8 @@ "isVisible": false }, "tooltip": { - "mode": "single" + "mode": "single", + "sort": "none" } }, "targets": [ @@ -193,7 +194,8 @@ "isVisible": false }, "tooltip": { - "mode": "single" + "mode": "single", + "sort": "none" } }, "targets": [ @@ -287,7 +289,8 @@ "isVisible": false }, "tooltip": { - "mode": "single" + "mode": "single", + "sort": "none" } }, "targets": [ @@ -382,7 +385,8 @@ "isVisible": false }, "tooltip": { - "mode": "single" + "mode": "single", + "sort": "none" } }, "targets": [ @@ -476,7 +480,8 @@ "isVisible": false }, "tooltip": { - "mode": "single" + "mode": "single", + "sort": "none" } }, "targets": [ @@ -579,7 +584,8 @@ "isVisible": false }, "tooltip": { - "mode": "single" + "mode": "single", + "sort": "none" } }, "targets": [ @@ -677,7 +683,8 @@ "isVisible": false }, "tooltip": { - "mode": "single" + "mode": "single", + "sort": "none" } }, "targets": [ @@ -775,7 +782,8 @@ "isVisible": false }, "tooltip": { - "mode": "single" + "mode": "single", + "sort": "none" } }, "targets": [ @@ -872,7 +880,8 @@ "isVisible": false }, "tooltip": { - "mode": "single" + "mode": "single", + "sort": "none" } }, "targets": [ diff --git a/pkg/schema/testdata/devenvgoldenfiles/graph-ng-nulls.json b/pkg/schema/testdata/devenvgoldenfiles/graph-ng-nulls.json index 47d9f5c0b20..b807575db92 100644 --- a/pkg/schema/testdata/devenvgoldenfiles/graph-ng-nulls.json +++ b/pkg/schema/testdata/devenvgoldenfiles/graph-ng-nulls.json @@ -103,7 +103,8 @@ "isVisible": false }, "tooltip": { - "mode": "single" + "mode": "single", + "sort": "none" } }, "pluginVersion": "7.4.0-pre", @@ -218,7 +219,8 @@ "isVisible": false }, "tooltip": { - "mode": "single" + "mode": "single", + "sort": "none" } }, "pluginVersion": "7.4.0-pre", @@ -341,7 +343,8 @@ "isVisible": false }, "tooltip": { - "mode": "single" + "mode": "single", + "sort": "none" } }, "pluginVersion": "7.4.0-pre", @@ -482,7 +485,8 @@ "isVisible": false }, "tooltip": { - "mode": "single" + "mode": "single", + "sort": "none" } }, "pluginVersion": "7.4.0-pre", @@ -597,7 +601,8 @@ "isVisible": false }, "tooltip": { - "mode": "single" + "mode": "single", + "sort": "none" } }, "pluginVersion": "7.4.0-pre", @@ -720,7 +725,8 @@ "isVisible": false }, "tooltip": { - "mode": "single" + "mode": "single", + "sort": "none" } }, "pluginVersion": "7.4.0-pre", @@ -891,7 +897,8 @@ "placement": "bottom" }, "tooltip": { - "mode": "single" + "mode": "single", + "sort": "none" } }, "pluginVersion": "7.2.0-pre", @@ -1037,7 +1044,8 @@ "placement": "bottom" }, "tooltip": { - "mode": "single" + "mode": "single", + "sort": "none" } }, "pluginVersion": "7.2.0-pre", @@ -1151,7 +1159,8 @@ "isVisible": false }, "tooltip": { - "mode": "single" + "mode": "single", + "sort": "none" } }, "pluginVersion": "7.5.0-pre", @@ -1262,7 +1271,8 @@ "isVisible": false }, "tooltip": { - "mode": "single" + "mode": "single", + "sort": "none" } }, "targets": [ diff --git a/pkg/schema/testdata/devenvgoldenfiles/new_features_in_v8.json b/pkg/schema/testdata/devenvgoldenfiles/new_features_in_v8.json index d500f61418a..f74b2db8f65 100644 --- a/pkg/schema/testdata/devenvgoldenfiles/new_features_in_v8.json +++ b/pkg/schema/testdata/devenvgoldenfiles/new_features_in_v8.json @@ -379,7 +379,8 @@ "isVisible": false }, "tooltip": { - "mode": "single" + "mode": "single", + "sort": "none" } }, "pluginVersion": "8.0.0-beta2", @@ -723,7 +724,8 @@ "showValue": "auto", "text": {}, "tooltip": { - "mode": "single" + "mode": "single", + "sort": "none" } }, "targets": [ @@ -853,7 +855,8 @@ "showValue": "auto", "text": {}, "tooltip": { - "mode": "single" + "mode": "single", + "sort": "none" } }, "targets": [ @@ -1034,7 +1037,8 @@ "isVisible": false }, "tooltip": { - "mode": "single" + "mode": "single", + "sort": "none" } }, "pluginVersion": "7.4.0-beta1", @@ -1161,7 +1165,8 @@ "isVisible": false }, "tooltip": { - "mode": "single" + "mode": "single", + "sort": "none" } }, "pluginVersion": "7.4.0-beta1", @@ -1288,7 +1293,8 @@ "isVisible": false }, "tooltip": { - "mode": "single" + "mode": "single", + "sort": "none" } }, "pluginVersion": "7.4.0-beta1", @@ -1437,7 +1443,8 @@ "isVisible": false }, "tooltip": { - "mode": "single" + "mode": "single", + "sort": "none" } }, "pluginVersion": "7.4.0-beta1", @@ -1550,7 +1557,8 @@ "isVisible": false }, "tooltip": { - "mode": "single" + "mode": "single", + "sort": "none" } }, "pluginVersion": "7.4.0-beta1", @@ -1663,7 +1671,8 @@ "isVisible": false }, "tooltip": { - "mode": "single" + "mode": "single", + "sort": "none" } }, "pluginVersion": "7.4.0-beta1", @@ -1865,7 +1874,8 @@ "isVisible": false }, "tooltip": { - "mode": "multi" + "mode": "multi", + "sort": "none" } }, "pluginVersion": "7.4.0-beta1", @@ -2163,7 +2173,8 @@ "isVisible": false }, "tooltip": { - "mode": "single" + "mode": "single", + "sort": "none" } }, "pluginVersion": "7.4.0-beta1", @@ -2494,7 +2505,8 @@ "isVisible": false }, "tooltip": { - "mode": "single" + "mode": "single", + "sort": "none" } }, "pluginVersion": "7.4.0-beta1", @@ -2757,7 +2769,8 @@ "isVisible": false }, "tooltip": { - "mode": "single" + "mode": "single", + "sort": "none" } }, "pluginVersion": "7.4.0-beta1", diff --git a/pkg/schema/testdata/devenvgoldenfiles/timeline-demo.json b/pkg/schema/testdata/devenvgoldenfiles/timeline-demo.json index ceacc01be4c..7931c5a6583 100644 --- a/pkg/schema/testdata/devenvgoldenfiles/timeline-demo.json +++ b/pkg/schema/testdata/devenvgoldenfiles/timeline-demo.json @@ -92,7 +92,8 @@ "rowHeight": 0.98, "showValue": "always", "tooltip": { - "mode": "single" + "mode": "single", + "sort": "none" } }, "pluginVersion": "7.5.0-pre", @@ -199,7 +200,8 @@ "rowHeight": 0.98, "showValue": "always", "tooltip": { - "mode": "single" + "mode": "single", + "sort": "none" } }, "targets": [ @@ -310,7 +312,8 @@ "rowHeight": 0.98, "showValue": "always", "tooltip": { - "mode": "single" + "mode": "single", + "sort": "none" } }, "targets": [ @@ -399,7 +402,8 @@ "rowHeight": 0.98, "showValue": "always", "tooltip": { - "mode": "single" + "mode": "single", + "sort": "none" } }, "pluginVersion": "7.5.0-pre", diff --git a/pkg/schema/testdata/devenvgoldenfiles/timeline-modes.json b/pkg/schema/testdata/devenvgoldenfiles/timeline-modes.json index 8d6c3b98f16..90a19f54522 100644 --- a/pkg/schema/testdata/devenvgoldenfiles/timeline-modes.json +++ b/pkg/schema/testdata/devenvgoldenfiles/timeline-modes.json @@ -71,7 +71,8 @@ "rowHeight": 0.9, "showValue": "always", "tooltip": { - "mode": "single" + "mode": "single", + "sort": "none" } }, "pluginVersion": "7.5.0-pre", @@ -256,7 +257,8 @@ "rowHeight": 0.9, "showValue": "always", "tooltip": { - "mode": "single" + "mode": "single", + "sort": "none" } }, "pluginVersion": "7.5.0-pre", @@ -340,7 +342,8 @@ "rowHeight": 0.9, "showValue": "always", "tooltip": { - "mode": "single" + "mode": "single", + "sort": "none" }, "alignValue": "left" }, diff --git a/public/app/features/explore/ExploreGraph.tsx b/public/app/features/explore/ExploreGraph.tsx index 8081253ffe3..431595553a9 100644 --- a/public/app/features/explore/ExploreGraph.tsx +++ b/public/app/features/explore/ExploreGraph.tsx @@ -16,7 +16,7 @@ import { TimeZone, } from '@grafana/data'; import { PanelRenderer } from '@grafana/runtime'; -import { GraphDrawStyle, LegendDisplayMode, TooltipDisplayMode } from '@grafana/schema'; +import { GraphDrawStyle, LegendDisplayMode, TooltipDisplayMode, SortOrder } from '@grafana/schema'; import { Icon, PanelContext, @@ -165,7 +165,7 @@ export function ExploreGraph({ timeZone={timeZone} options={ { - tooltip: { mode: tooltipDisplayMode }, + tooltip: { mode: tooltipDisplayMode, sort: SortOrder.None }, legend: { displayMode: LegendDisplayMode.List, placement: 'bottom', calcs: [] }, } as TimeSeriesOptions } diff --git a/public/app/plugins/panel/barchart/BarChartPanel.tsx b/public/app/plugins/panel/barchart/BarChartPanel.tsx index b0920763bf6..e529d616be1 100755 --- a/public/app/plugins/panel/barchart/BarChartPanel.tsx +++ b/public/app/plugins/panel/barchart/BarChartPanel.tsx @@ -118,7 +118,14 @@ export const BarChartPanel: React.FunctionComponent = ({ data, options, w seriesIdx = info.aligned.fields.findIndex((f) => disp === getFieldDisplayName(f, info.aligned)); } - return ; + return ( + + ); }; const renderLegend = (config: UPlotConfigBuilder) => { diff --git a/public/app/plugins/panel/barchart/utils.test.ts b/public/app/plugins/panel/barchart/utils.test.ts index b9fa83e0d8f..482c9c09f47 100644 --- a/public/app/plugins/panel/barchart/utils.test.ts +++ b/public/app/plugins/panel/barchart/utils.test.ts @@ -5,6 +5,7 @@ import { VisibilityMode, GraphGradientMode, StackingMode, + SortOrder, } from '@grafana/schema'; import { createTheme, @@ -92,6 +93,7 @@ describe('BarChart utils', () => { stacking: StackingMode.None, tooltip: { mode: TooltipDisplayMode.None, + sort: SortOrder.None, }, text: { valueSize: 10, diff --git a/public/app/plugins/panel/geomap/components/DataHoverView.tsx b/public/app/plugins/panel/geomap/components/DataHoverView.tsx index 4740175ddf3..cdc5f1efdcf 100644 --- a/public/app/plugins/panel/geomap/components/DataHoverView.tsx +++ b/public/app/plugins/panel/geomap/components/DataHoverView.tsx @@ -2,6 +2,7 @@ import React, { PureComponent } from 'react'; import { stylesFactory } from '@grafana/ui'; import { ArrayDataFrame, + arrayUtils, DataFrame, Field, formattedValueToString, @@ -11,19 +12,21 @@ import { import { css } from '@emotion/css'; import { config } from 'app/core/config'; import { FeatureLike } from 'ol/Feature'; +import { SortOrder } from '@grafana/schema'; export interface Props { data?: DataFrame; // source data feature?: FeatureLike; rowIndex?: number | null; // the hover row columnIndex?: number | null; // the hover column + sortOrder?: SortOrder; } export class DataHoverView extends PureComponent { style = getStyles(config.theme2); render() { - const { feature, columnIndex } = this.props; + const { feature, columnIndex, sortOrder } = this.props; let { data, rowIndex } = this.props; if (feature) { const { geometry, ...properties } = feature.getProperties(); @@ -35,17 +38,33 @@ export class DataHoverView extends PureComponent { return null; } + const displayValues: Array<[string, any, string]> = []; + const visibleFields = data.fields.filter((f) => !Boolean(f.config.custom?.hideFrom?.tooltip)); + + if (visibleFields.length === 0) { + return null; + } + for (let i = 0; i < visibleFields.length; i++) { + displayValues.push([ + getFieldDisplayName(visibleFields[i], data), + visibleFields[i].values.get(rowIndex!), + fmt(visibleFields[i], rowIndex), + ]); + } + + if (sortOrder && sortOrder !== SortOrder.None) { + displayValues.sort((a, b) => arrayUtils.sortValues(sortOrder)(a[1], b[1])); + } + return ( - {data.fields - .filter((f) => !Boolean(f.config.custom?.hideFrom?.tooltip)) - .map((f, i) => ( - - - - - ))} + {displayValues.map((v, i) => ( + + + + + ))}
{getFieldDisplayName(f, data)}:{fmt(f, rowIndex!)}
{v[0]}:{v[2]}
); diff --git a/public/app/plugins/panel/histogram/models.gen.ts b/public/app/plugins/panel/histogram/models.gen.ts index 8b1c0cfb095..1b1b9315c44 100644 --- a/public/app/plugins/panel/histogram/models.gen.ts +++ b/public/app/plugins/panel/histogram/models.gen.ts @@ -10,6 +10,7 @@ import { TooltipDisplayMode, GraphGradientMode, HideableFieldConfig, + SortOrder, } from '@grafana/schema'; export const modelVersion = Object.freeze([1, 0]); @@ -29,6 +30,7 @@ export const defaultPanelOptions: PanelOptions = { }, tooltip: { mode: TooltipDisplayMode.Multi, + sort: SortOrder.None, }, }; diff --git a/public/app/plugins/panel/timeseries/TimeSeriesPanel.tsx b/public/app/plugins/panel/timeseries/TimeSeriesPanel.tsx index 725c13988cf..d347abe441a 100644 --- a/public/app/plugins/panel/timeseries/TimeSeriesPanel.tsx +++ b/public/app/plugins/panel/timeseries/TimeSeriesPanel.tsx @@ -60,6 +60,7 @@ export const TimeSeriesPanel: React.FC = ({ data={alignedDataFrame} config={config} mode={options.tooltip.mode} + sortOrder={options.tooltip.sort} sync={sync} timeZone={timeZone} /> diff --git a/public/app/plugins/panel/timeseries/__snapshots__/migrations.test.ts.snap b/public/app/plugins/panel/timeseries/__snapshots__/migrations.test.ts.snap index fb462dab015..fbebe31cfa9 100644 --- a/public/app/plugins/panel/timeseries/__snapshots__/migrations.test.ts.snap +++ b/public/app/plugins/panel/timeseries/__snapshots__/migrations.test.ts.snap @@ -46,7 +46,8 @@ Object { "placement": "bottom", }, "tooltip": Object { - "mode": "single", + "mode": "multi", + "sort": "none", }, }, } @@ -74,6 +75,7 @@ Object { }, "tooltip": Object { "mode": "single", + "sort": "none", }, }, } @@ -99,6 +101,7 @@ Object { }, "tooltip": Object { "mode": "single", + "sort": "none", }, }, } @@ -153,7 +156,8 @@ Object { "placement": "bottom", }, "tooltip": Object { - "mode": "single", + "mode": "multi", + "sort": "none", }, }, } @@ -208,7 +212,8 @@ Object { "placement": "bottom", }, "tooltip": Object { - "mode": "single", + "mode": "multi", + "sort": "none", }, }, } @@ -236,6 +241,7 @@ Object { }, "tooltip": Object { "mode": "single", + "sort": "none", }, }, } @@ -321,7 +327,8 @@ Object { "placement": "bottom", }, "tooltip": Object { - "mode": "single", + "mode": "multi", + "sort": "none", }, }, } @@ -377,7 +384,8 @@ Object { "placement": "bottom", }, "tooltip": Object { - "mode": "single", + "mode": "multi", + "sort": "none", }, }, } @@ -417,7 +425,8 @@ Object { "placement": "bottom", }, "tooltip": Object { - "mode": "single", + "mode": "multi", + "sort": "none", }, }, } @@ -465,7 +474,8 @@ Object { "placement": "bottom", }, "tooltip": Object { - "mode": "single", + "mode": "multi", + "sort": "none", }, }, } @@ -550,7 +560,8 @@ Object { "placement": "bottom", }, "tooltip": Object { - "mode": "single", + "mode": "multi", + "sort": "none", }, }, } diff --git a/public/app/plugins/panel/timeseries/migrations.test.ts b/public/app/plugins/panel/timeseries/migrations.test.ts index cf7279417df..a2a56526537 100644 --- a/public/app/plugins/panel/timeseries/migrations.test.ts +++ b/public/app/plugins/panel/timeseries/migrations.test.ts @@ -1,6 +1,7 @@ import { PanelModel, FieldConfigSource } from '@grafana/data'; import { graphPanelChangedHandler } from './migrations'; import { cloneDeep } from 'lodash'; +import { TooltipDisplayMode, SortOrder } from '@grafana/schema'; describe('Graph Migrations', () => { let prevFieldConfig: FieldConfigSource; @@ -308,6 +309,87 @@ describe('Graph Migrations', () => { expect(panel.fieldConfig.overrides[0].properties[0].value).toEqual({ viz: true, legend: false, tooltip: false }); }); }); + + describe('tooltip', () => { + test('tooltip mode', () => { + const single: any = { + angular: { + tooltip: { + shared: false, + }, + }, + }; + const multi: any = { + angular: { + tooltip: { + shared: true, + }, + }, + }; + + const panel1 = {} as PanelModel; + const panel2 = {} as PanelModel; + + panel1.options = graphPanelChangedHandler(panel1, 'graph', single, prevFieldConfig); + panel2.options = graphPanelChangedHandler(panel2, 'graph', multi, prevFieldConfig); + + expect(panel1.options.tooltip.mode).toBe(TooltipDisplayMode.Single); + expect(panel2.options.tooltip.mode).toBe(TooltipDisplayMode.Multi); + }); + + test('sort order', () => { + const none: any = { + angular: { + tooltip: { + shared: true, + sort: 0, + }, + }, + }; + + const asc: any = { + angular: { + tooltip: { + shared: true, + sort: 1, + }, + }, + }; + + const desc: any = { + angular: { + tooltip: { + shared: true, + sort: 2, + }, + }, + }; + + const singleModeWithUnnecessaryOption: any = { + angular: { + tooltip: { + shared: false, + sort: 2, + }, + }, + }; + + const panel1 = {} as PanelModel; + const panel2 = {} as PanelModel; + const panel3 = {} as PanelModel; + const panel4 = {} as PanelModel; + + panel1.options = graphPanelChangedHandler(panel1, 'graph', none, prevFieldConfig); + panel2.options = graphPanelChangedHandler(panel2, 'graph', asc, prevFieldConfig); + panel3.options = graphPanelChangedHandler(panel3, 'graph', desc, prevFieldConfig); + panel4.options = graphPanelChangedHandler(panel4, 'graph', singleModeWithUnnecessaryOption, prevFieldConfig); + + expect(panel1.options.tooltip.sort).toBe(SortOrder.None); + expect(panel2.options.tooltip.sort).toBe(SortOrder.Ascending); + expect(panel3.options.tooltip.sort).toBe(SortOrder.Descending); + expect(panel4.options.tooltip.sort).toBe(SortOrder.None); + }); + }); }); const customColor = { diff --git a/public/app/plugins/panel/timeseries/migrations.ts b/public/app/plugins/panel/timeseries/migrations.ts index 8e80f853389..91d01aa2957 100644 --- a/public/app/plugins/panel/timeseries/migrations.ts +++ b/public/app/plugins/panel/timeseries/migrations.ts @@ -25,6 +25,7 @@ import { VisibilityMode, ScaleDistribution, StackingMode, + SortOrder, } from '@grafana/schema'; import { TimeSeriesOptions } from './types'; import { omitBy, pickBy, isNil, isNumber, isString } from 'lodash'; @@ -313,6 +314,7 @@ export function flotToGraphOptions(angular: any): { fieldConfig: FieldConfigSour }, tooltip: { mode: TooltipDisplayMode.Single, + sort: SortOrder.None, }, }; @@ -335,6 +337,26 @@ export function flotToGraphOptions(angular: any): { fieldConfig: FieldConfigSour } } + const tooltipConfig = angular.tooltip; + if (tooltipConfig) { + if (tooltipConfig.shared !== undefined) { + options.tooltip.mode = tooltipConfig.shared ? TooltipDisplayMode.Multi : TooltipDisplayMode.Single; + } + + if (tooltipConfig.sort !== undefined && tooltipConfig.shared) { + switch (tooltipConfig.sort) { + case 1: + options.tooltip.sort = SortOrder.Ascending; + break; + case 2: + options.tooltip.sort = SortOrder.Descending; + break; + default: + options.tooltip.sort = SortOrder.None; + } + } + } + if (angular.thresholds && angular.thresholds.length > 0) { let steps: Threshold[] = []; let area = false;