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>
pull/37771/head
nikki-kiga 4 years ago committed by GitHub
parent 5986d99f51
commit 87b8a74576
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 17
      packages/grafana-data/src/field/displayProcessor.ts
  2. 69
      packages/grafana-data/src/field/getFieldDisplayValuesProxy.test.tsx
  3. 10
      packages/grafana-data/src/valueFormats/dateTimeFormatters.ts
  4. 3
      packages/grafana-data/src/valueFormats/valueFormats.ts
  5. 12
      packages/grafana-ui/src/components/Graph/utils.test.ts

@ -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' } },
},
],

Loading…
Cancel
Save