mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Tooltip: display ms within minute time range (#37569)
* Tooltip: add hasMs flag for system dateTime format * Tooltip: display ms in sub minute time range * Tooltip: add field check, increase range on tests * Add diffrentiating test * minimize parsing to string values Co-authored-by: Oscar Kilhed <oscar.kilhed@grafana.com>
This commit is contained in:
@@ -6,7 +6,7 @@ import { Field, FieldType } from '../types/dataFrame';
|
||||
import { DisplayProcessor, DisplayValue } from '../types/displayValue';
|
||||
import { getValueFormat, isBooleanUnit } from '../valueFormats/valueFormats';
|
||||
import { getValueMappingResult } from '../utils/valueMappings';
|
||||
import { dateTime } from '../datetime';
|
||||
import { dateTime, dateTimeParse } from '../datetime';
|
||||
import { KeyValue, TimeZone } from '../types';
|
||||
import { getScaleCalculator } from './scale';
|
||||
import { GrafanaTheme2 } from '../themes/types';
|
||||
@@ -45,10 +45,23 @@ export function getDisplayProcessor(options?: DisplayProcessorOptions): DisplayP
|
||||
|
||||
let unit = config.unit;
|
||||
let hasDateUnit = unit && (timeFormats[unit] || unit.startsWith('time:'));
|
||||
let showMs = false;
|
||||
|
||||
if (field.type === FieldType.time && !hasDateUnit) {
|
||||
unit = `dateTimeAsSystem`;
|
||||
hasDateUnit = true;
|
||||
if (field.values && field.values.length > 1) {
|
||||
let start = field.values.get(0);
|
||||
let end = field.values.get(field.values.length - 1);
|
||||
if (typeof start === 'string') {
|
||||
start = dateTimeParse(start).unix();
|
||||
end = dateTimeParse(end).unix();
|
||||
} else {
|
||||
start /= 1e3;
|
||||
end /= 1e3;
|
||||
}
|
||||
showMs = end - start < 60; //show ms when minute or less
|
||||
}
|
||||
} else if (field.type === FieldType.boolean) {
|
||||
if (!isBooleanUnit(unit)) {
|
||||
unit = 'bool';
|
||||
@@ -93,7 +106,7 @@ export function getDisplayProcessor(options?: DisplayProcessorOptions): DisplayP
|
||||
|
||||
if (!isNaN(numeric)) {
|
||||
if (shouldFormat && !isBoolean(value)) {
|
||||
const v = formatFunc(numeric, config.decimals, null, options.timeZone);
|
||||
const v = formatFunc(numeric, config.decimals, null, options.timeZone, showMs);
|
||||
text = v.text;
|
||||
suffix = v.suffix;
|
||||
prefix = v.prefix;
|
||||
|
||||
@@ -4,28 +4,25 @@ import { toDataFrame } from '../dataframe';
|
||||
import { createTheme } from '../themes';
|
||||
|
||||
describe('getFieldDisplayValuesProxy', () => {
|
||||
const data = applyFieldOverrides({
|
||||
data: [
|
||||
toDataFrame({
|
||||
fields: [
|
||||
{ name: 'Time', values: [1, 2, 3] },
|
||||
{
|
||||
name: 'power',
|
||||
values: [100, 200, 300],
|
||||
labels: {
|
||||
name: 'POWAH!',
|
||||
},
|
||||
config: {
|
||||
displayName: 'The Power',
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'Last',
|
||||
values: ['a', 'b', 'c'],
|
||||
},
|
||||
],
|
||||
}),
|
||||
],
|
||||
const shortTimeField = [{ name: 'Time', values: [1, 2, 3] }];
|
||||
|
||||
const longTimeField = [{ name: 'Time', values: [1000, 2000, 61000] }];
|
||||
|
||||
const dataFields = [
|
||||
{
|
||||
name: 'power',
|
||||
values: [100, 200, 300],
|
||||
labels: {
|
||||
name: 'POWAH!',
|
||||
},
|
||||
config: {
|
||||
displayName: 'The Power',
|
||||
},
|
||||
},
|
||||
{ name: 'Last', values: ['a', 'b', 'c'] },
|
||||
];
|
||||
|
||||
const overrides = {
|
||||
fieldConfig: {
|
||||
defaults: {},
|
||||
overrides: [],
|
||||
@@ -33,29 +30,45 @@ describe('getFieldDisplayValuesProxy', () => {
|
||||
replaceVariables: (val: string) => val,
|
||||
timeZone: 'utc',
|
||||
theme: createTheme(),
|
||||
};
|
||||
|
||||
const dataShortTimeRange = applyFieldOverrides({
|
||||
...{ data: [toDataFrame({ fields: [...shortTimeField, ...dataFields] })] },
|
||||
...overrides,
|
||||
})[0];
|
||||
|
||||
const dataLongTimeRange = applyFieldOverrides({
|
||||
...{ data: [toDataFrame({ fields: [...longTimeField, ...dataFields] })] },
|
||||
...overrides,
|
||||
})[0];
|
||||
|
||||
it('should define all display functions', () => {
|
||||
// Field display should be set
|
||||
for (const field of data.fields) {
|
||||
for (const field of dataShortTimeRange.fields) {
|
||||
expect(field.display).toBeDefined();
|
||||
}
|
||||
});
|
||||
|
||||
it('should format the time values in UTC', () => {
|
||||
it('should format the time values in UTC with ms when time range is minute or less', () => {
|
||||
// Test Proxies in general
|
||||
const p = getFieldDisplayValuesProxy({ frame: data, rowIndex: 0 });
|
||||
const p = getFieldDisplayValuesProxy({ frame: dataShortTimeRange, rowIndex: 0 });
|
||||
const time = p.Time;
|
||||
expect(time.numeric).toEqual(1);
|
||||
expect(time.text).toEqual('1970-01-01 00:00:00');
|
||||
expect(time.text).toEqual('1970-01-01 00:00:00.001');
|
||||
|
||||
// Should get to the same values by name or index
|
||||
const time2 = p[0];
|
||||
expect(time2.toString()).toEqual(time.toString());
|
||||
});
|
||||
|
||||
it('should format the time values in UTC without ms when time range is over a minute', () => {
|
||||
const p = getFieldDisplayValuesProxy({ frame: dataLongTimeRange, rowIndex: 0 });
|
||||
const time = p.Time;
|
||||
expect(time.text).toEqual('1970-01-01 00:00:01');
|
||||
});
|
||||
|
||||
it('Lookup by name, index, or displayName', () => {
|
||||
const p = getFieldDisplayValuesProxy({ frame: data, rowIndex: 2 });
|
||||
const p = getFieldDisplayValuesProxy({ frame: dataShortTimeRange, rowIndex: 2 });
|
||||
expect(p.power.numeric).toEqual(300);
|
||||
expect(p['power'].numeric).toEqual(300);
|
||||
expect(p['POWAH!'].numeric).toEqual(300);
|
||||
@@ -64,7 +77,7 @@ describe('getFieldDisplayValuesProxy', () => {
|
||||
});
|
||||
|
||||
it('should return undefined when missing', () => {
|
||||
const p = getFieldDisplayValuesProxy({ frame: data, rowIndex: 0 });
|
||||
const p = getFieldDisplayValuesProxy({ frame: dataShortTimeRange, rowIndex: 0 });
|
||||
expect(p.xyz).toBeUndefined();
|
||||
expect(p[100]).toBeUndefined();
|
||||
});
|
||||
|
||||
@@ -405,9 +405,15 @@ export function dateTimeSystemFormatter(
|
||||
value: number,
|
||||
decimals: DecimalCount,
|
||||
scaledDecimals: DecimalCount,
|
||||
timeZone?: TimeZone
|
||||
timeZone?: TimeZone,
|
||||
showMs?: boolean
|
||||
): FormattedValue {
|
||||
return { text: dateTimeFormat(value, { format: systemDateFormats.fullDate, timeZone }) };
|
||||
return {
|
||||
text: dateTimeFormat(value, {
|
||||
format: showMs ? systemDateFormats.fullDateMS : systemDateFormats.fullDate,
|
||||
timeZone,
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
export function dateTimeFromNow(
|
||||
|
||||
@@ -18,7 +18,8 @@ export type ValueFormatter = (
|
||||
value: number,
|
||||
decimals?: DecimalCount,
|
||||
scaledDecimals?: DecimalCount,
|
||||
timeZone?: TimeZone
|
||||
timeZone?: TimeZone,
|
||||
showMs?: boolean
|
||||
) => FormattedValue;
|
||||
|
||||
export interface ValueFormat {
|
||||
|
||||
@@ -45,11 +45,11 @@ function passThroughFieldOverrides(frame: DataFrame) {
|
||||
const aSeries = passThroughFieldOverrides(
|
||||
toDataFrame({
|
||||
fields: [
|
||||
{ name: 'time', type: FieldType.time, values: [10000, 20000, 30000] },
|
||||
{ name: 'time', type: FieldType.time, values: [10000, 20000, 30000, 80000] },
|
||||
{
|
||||
name: 'value',
|
||||
type: FieldType.number,
|
||||
values: [10, 20, 10],
|
||||
values: [10, 20, 10, 25],
|
||||
config: { color: { mode: FieldColorModeId.Fixed, fixedColor: 'red' } },
|
||||
},
|
||||
],
|
||||
@@ -59,11 +59,11 @@ const aSeries = passThroughFieldOverrides(
|
||||
const bSeries = passThroughFieldOverrides(
|
||||
toDataFrame({
|
||||
fields: [
|
||||
{ name: 'time', type: FieldType.time, values: [10000, 20000, 30000] },
|
||||
{ name: 'time', type: FieldType.time, values: [10000, 20000, 30000, 80000] },
|
||||
{
|
||||
name: 'value',
|
||||
type: FieldType.number,
|
||||
values: [30, 60, 30],
|
||||
values: [30, 60, 30, 40],
|
||||
config: { color: { mode: FieldColorModeId.Fixed, fixedColor: 'blue' } },
|
||||
},
|
||||
],
|
||||
@@ -74,11 +74,11 @@ const bSeries = passThroughFieldOverrides(
|
||||
const cSeries = passThroughFieldOverrides(
|
||||
toDataFrame({
|
||||
fields: [
|
||||
{ name: 'time', type: FieldType.time, values: [10000, 30000] },
|
||||
{ name: 'time', type: FieldType.time, values: [10000, 30000, 80000] },
|
||||
{
|
||||
name: 'value',
|
||||
type: FieldType.number,
|
||||
values: [30, 30],
|
||||
values: [30, 30, 30],
|
||||
config: { color: { mode: FieldColorModeId.Fixed, fixedColor: 'yellow' } },
|
||||
},
|
||||
],
|
||||
|
||||
Reference in New Issue
Block a user