From 5d3f4422c57236293fd309f4593898361b4760fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Thu, 3 Jan 2019 09:39:46 +0100 Subject: [PATCH 01/26] initial design for way to build value formats lazily and a backward compatability layer via Proxy --- packages/grafana-ui/src/utils/index.ts | 1 + packages/grafana-ui/src/utils/valueFormats.ts | 104 ++++++++++++++++++ public/app/core/utils/kbn.ts | 21 ++++ .../app/plugins/panel/graph2/GraphPanel.tsx | 10 +- 4 files changed, 134 insertions(+), 2 deletions(-) create mode 100644 packages/grafana-ui/src/utils/valueFormats.ts diff --git a/packages/grafana-ui/src/utils/index.ts b/packages/grafana-ui/src/utils/index.ts index 4d9b9a4b948..cd46a5b6424 100644 --- a/packages/grafana-ui/src/utils/index.ts +++ b/packages/grafana-ui/src/utils/index.ts @@ -1 +1,2 @@ export * from './processTimeSeries'; +export * from './valueFormats'; diff --git a/packages/grafana-ui/src/utils/valueFormats.ts b/packages/grafana-ui/src/utils/valueFormats.ts new file mode 100644 index 00000000000..d76db20fb0b --- /dev/null +++ b/packages/grafana-ui/src/utils/valueFormats.ts @@ -0,0 +1,104 @@ +type ValueFormatter = (value: number, decimals?: number, scaledDecimals?: number) => string; + +interface ValueFormat { + name: string; + id: string; + fn: ValueFormatter; +} + +interface ValueFormatCategory { + name: string; + formats: ValueFormat[]; +} + +interface ValueFormatterIndex { + [id: string]: ValueFormatter; +} + +// Globals & formats cache +let categories: ValueFormatCategory[] = []; +const index: ValueFormatterIndex = {}; +let hasBuildIndex = false; + +function toFixed(value: number, decimals?: number): string { + if (value === null) { + return ''; + } + + const factor = decimals ? Math.pow(10, Math.max(0, decimals)) : 1; + const formatted = String(Math.round(value * factor) / factor); + + // if exponent return directly + if (formatted.indexOf('e') !== -1 || value === 0) { + return formatted; + } + + // If tickDecimals was specified, ensure that we have exactly that + // much precision; otherwise default to the value's own precision. + if (decimals != null) { + const decimalPos = formatted.indexOf('.'); + const precision = decimalPos === -1 ? 0 : formatted.length - decimalPos - 1; + if (precision < decimals) { + return (precision ? formatted : formatted + '.') + String(factor).substr(1, decimals - precision); + } + } + + return formatted; +} + +function buildFormats() { + categories = [ + { + name: 'none', + formats: [ + { + name: 'short', + id: 'short', + fn: toFixed, + }, + ], + }, + ]; + + for (const cat of categories) { + for (const format of cat.formats) { + index[format.id] = format.fn; + } + } + + hasBuildIndex = true; +} + +export function getValueFormat(id: string): ValueFormatter { + if (!hasBuildIndex) { + buildFormats(); + } + + return index[id]; +} + +export function getValueFormatterIndex(): ValueFormatterIndex { + if (!hasBuildIndex) { + buildFormats(); + } + + return index; +} + +export function getUnitFormats() { + if (!hasBuildIndex) { + buildFormats(); + } + + return categories.map(cat => { + return { + text: cat.name, + submenu: cat.formats.map(format => { + return { + text: format.name, + value: format.id, + }; + }), + }; + }); +} diff --git a/public/app/core/utils/kbn.ts b/public/app/core/utils/kbn.ts index d32844c44ed..2fae6300d16 100644 --- a/public/app/core/utils/kbn.ts +++ b/public/app/core/utils/kbn.ts @@ -1,5 +1,6 @@ import _ from 'lodash'; import moment from 'moment'; +import { getValueFormat, getValueFormatterIndex } from '@grafana/ui'; const kbn: any = {}; @@ -1218,4 +1219,24 @@ kbn.getUnitFormats = () => { ]; }; +if (typeof Proxy !== "undefined") { + kbn.valueFormats = new Proxy(kbn.valueFormats, { + get(target, name, receiver) { + if (typeof name !== 'string') { + throw {message: `Value format ${String(name)} is not a string` }; + } + + const formatter = getValueFormat(name); + if (formatter) { + return formatter; + } + + // default to look here + return Reflect.get(target, name, receiver); + } + }); +} else { + kbn.valueFormats = getValueFormatterIndex(); +} + export default kbn; diff --git a/public/app/plugins/panel/graph2/GraphPanel.tsx b/public/app/plugins/panel/graph2/GraphPanel.tsx index a08276e5179..3429200d52b 100644 --- a/public/app/plugins/panel/graph2/GraphPanel.tsx +++ b/public/app/plugins/panel/graph2/GraphPanel.tsx @@ -3,8 +3,14 @@ import _ from 'lodash'; import React, { PureComponent } from 'react'; import colors from 'app/core/utils/colors'; -// Components & Types -import { Graph, PanelProps, NullValueMode, processTimeSeries } from '@grafana/ui'; +// Utils +import { processTimeSeries } from '@grafana/ui/src/utils'; + +// Components +import { Graph } from '@grafana/ui'; + +// Types +import { PanelProps, NullValueMode } from '@grafana/ui/src/types'; import { Options } from './types'; interface Props extends PanelProps {} From ffc9b7ac03e8e8b161b8280ae66d3cca2b0f8710 Mon Sep 17 00:00:00 2001 From: Peter Holmberg Date: Fri, 4 Jan 2019 15:45:39 +0100 Subject: [PATCH 02/26] first stuff --- packages/grafana-ui/src/utils/valueFormats.ts | 167 +++++++++++++++++- 1 file changed, 165 insertions(+), 2 deletions(-) diff --git a/packages/grafana-ui/src/utils/valueFormats.ts b/packages/grafana-ui/src/utils/valueFormats.ts index d76db20fb0b..cdff409a683 100644 --- a/packages/grafana-ui/src/utils/valueFormats.ts +++ b/packages/grafana-ui/src/utils/valueFormats.ts @@ -46,16 +46,179 @@ function toFixed(value: number, decimals?: number): string { return formatted; } +function toFixedUnit(unit: string) { + return (size: number, decimals: number) => { + if (size === null) { + return ''; + } + return toFixed(size, decimals) + ' ' + unit; + }; +} + +// Formatter which scales the unit string geometrically according to the given +// numeric factor. Repeatedly scales the value down by the factor until it is +// less than the factor in magnitude, or the end of the array is reached. +function scaledUnits(factor: number, extArray: string[]) { + return (size: number, decimals: number, scaledDecimals: number) => { + if (size === null) { + return ''; + } + + let steps = 0; + const limit = extArray.length; + + while (Math.abs(size) >= factor) { + steps++; + size /= factor; + + if (steps >= limit) { + return 'NA'; + } + } + + if (steps > 0 && scaledDecimals !== null) { + decimals = scaledDecimals + 3 * steps; + } + + return toFixed(size, decimals) + extArray[steps]; + }; +} + +function toPercent(size: number, decimals: number) { + if (size === null) { + return ''; + } + return toFixed(size, decimals) + '%'; +} + +function toPercentUnit(size: number, decimals: number) { + if (size === null) { + return ''; + } + return toFixed(100 * size, decimals) + '%'; +} + +function toHex0x(value: number, decimals: number) { + if (value == null) { + return ''; + } + const hexString = hex(value, decimals); + if (hexString.substring(0, 1) === '-') { + return '-0x' + hexString.substring(1); + } + return '0x' + hexString; +} + +function hex(value: number, decimals: number) { + if (value == null) { + return ''; + } + return parseFloat(toFixed(value, decimals)) + .toString(16) + .toUpperCase(); +} + +function sci(value: number, decimals: number) { + if (value == null) { + return ''; + } + return value.toExponential(decimals); +} + +function locale(value: number, decimals: number) { + if (value == null) { + return ''; + } + return value.toLocaleString(undefined, { maximumFractionDigits: decimals }); +} + +function currency(symbol: string) { + const units = ['', 'K', 'M', 'B', 'T']; + const scaler = scaledUnits(1000, units); + return (size: number, decimals: number, scaledDecimals: number) => { + if (size === null) { + return ''; + } + const scaled = scaler(size, decimals, scaledDecimals); + return symbol + scaled; + }; +} + function buildFormats() { categories = [ { name: 'none', formats: [ { - name: 'short', - id: 'short', + name: 'none', + id: 'none', fn: toFixed, }, + { + name: 'short', + id: 'short', + fn: scaledUnits(1000, ['', ' K', ' Mil', ' Bil', ' Tri', ' Quadr', ' Quint', ' Sext', ' Sept']), + }, + { + name: 'percent (0-100)', + id: 'percent', + fn: toPercent, + }, + { + name: 'percent (0.0-1.0)', + id: 'percentunit', + fn: toPercentUnit, + }, + { + name: 'Humidity (%H)', + id: 'humidity', + fn: toFixedUnit('%H'), + }, + { + name: 'decibel', + id: 'dB', + fn: toFixedUnit('dB'), + }, + { + name: 'hexadecimal (0x)', + id: 'hex0x', + fn: toHex0x, + }, + { + name: 'hexadecimal', + id: 'hex', + fn: hex, + }, + { + name: 'scientific notation', + id: 'sci', + fn: sci, + }, + { + name: 'locale format', + id: 'locale', + fn: locale, + }, + ], + }, + { + name: 'currency', + formats: [ + { name: 'Dollars ($)', id: 'currencyUSD', fn: currency('$') }, + { name: 'Pounds (£)', id: 'currencyGBP', fn: currency('£') }, + { name: 'Euro (€)', id: 'currencyEUR', fn: currency('€') }, + { name: 'Yen (¥)', id: 'currencyJPY', fn: currency('¥') }, + { name: 'Rubles (₽)', id: 'currencyRUB', fn: currency('₽') }, + { name: 'Hryvnias (₴)', id: 'currencyUAH', fn: currency('₴') }, + { name: 'Real (R$)', id: 'currencyBRL', fn: currency('R$') }, + { name: 'Danish Krone (kr)', id: 'currencyDKK', fn: currency('kr') }, + { name: 'Icelandic Króna (kr)', id: 'currencyISK', fn: currency('kr') }, + { name: 'Norwegian Krone (kr)', id: 'currencyNOK', fn: currency('kr') }, + { name: 'Swedish Krona (kr)', id: 'currencySEK', fn: currency('kr') }, + { name: 'Czech koruna (czk)', id: 'currencyCZK', fn: currency('czk') }, + { name: 'Swiss franc (CHF)', id: 'currencyCHF', fn: currency('CHF') }, + { name: 'Polish Złoty (PLN)', id: 'currencyPLN', fn: currency('PLN') }, + { name: 'Bitcoin (฿)', id: 'currencyBTC', fn: currency('฿') }, ], }, ]; From 36e4bf598541b5961f70816b907f20147d996b87 Mon Sep 17 00:00:00 2001 From: Peter Holmberg Date: Mon, 7 Jan 2019 17:00:10 +0100 Subject: [PATCH 03/26] adding more units and functions --- packages/grafana-ui/src/utils/valueFormats.ts | 571 ++++++++++++++++-- 1 file changed, 524 insertions(+), 47 deletions(-) diff --git a/packages/grafana-ui/src/utils/valueFormats.ts b/packages/grafana-ui/src/utils/valueFormats.ts index cdff409a683..c7c8dbd447b 100644 --- a/packages/grafana-ui/src/utils/valueFormats.ts +++ b/packages/grafana-ui/src/utils/valueFormats.ts @@ -1,4 +1,6 @@ -type ValueFormatter = (value: number, decimals?: number, scaledDecimals?: number) => string; +import moment from 'moment'; + +type ValueFormatter = (value: number, decimals?: number, scaledDecimals?: number, isUtc?: boolean) => string; interface ValueFormat { name: string; @@ -15,6 +17,32 @@ interface ValueFormatterIndex { [id: string]: ValueFormatter; } +interface IntervalsInSeconds { + [interval: string]: number; +} + +enum Interval { + Year = 'year', + Month = 'month', + Week = 'week', + Day = 'day', + Hour = 'hour', + Minute = 'minute', + Second = 'second', + Millisecond = 'millisecond', +} + +const INTERVALS_IN_SECONDS: IntervalsInSeconds = { + [Interval.Year]: 31536000, + [Interval.Month]: 2592000, + [Interval.Week]: 604800, + [Interval.Day]: 86400, + [Interval.Hour]: 3600, + [Interval.Month]: 60, + [Interval.Second]: 1, + [Interval.Millisecond]: 0.001, +}; + // Globals & formats cache let categories: ValueFormatCategory[] = []; const index: ValueFormatterIndex = {}; @@ -46,6 +74,20 @@ function toFixed(value: number, decimals?: number): string { return formatted; } +function toFixedScaled( + value: number, + decimals: number, + scaledDecimals: number, + additionalDecimals: number, + ext: string +) { + if (scaledDecimals === null) { + return toFixed(value, decimals) + ext; + } else { + return toFixed(value, scaledDecimals + additionalDecimals) + ext; + } +} + function toFixedUnit(unit: string) { return (size: number, decimals: number) => { if (size === null) { @@ -144,61 +186,345 @@ function currency(symbol: string) { }; } +function toNanoSeconds(size: number, decimals: number, scaledDecimals: number) { + if (size === null) { + return ''; + } + + if (Math.abs(size) < 1000) { + return toFixed(size, decimals) + ' ns'; + } else if (Math.abs(size) < 1000000) { + return toFixedScaled(size / 1000, decimals, scaledDecimals, 3, ' µs'); + } else if (Math.abs(size) < 1000000000) { + return toFixedScaled(size / 1000000, decimals, scaledDecimals, 6, ' ms'); + } else if (Math.abs(size) < 60000000000) { + return toFixedScaled(size / 1000000000, decimals, scaledDecimals, 9, ' s'); + } else { + return toFixedScaled(size / 60000000000, decimals, scaledDecimals, 12, ' min'); + } +} + +function toMicroSeconds(size: number, decimals: number, scaledDecimals: number) { + if (size === null) { + return ''; + } + + if (Math.abs(size) < 1000) { + return toFixed(size, decimals) + ' µs'; + } else if (Math.abs(size) < 1000000) { + return toFixedScaled(size / 1000, decimals, scaledDecimals, 3, ' ms'); + } else { + return toFixedScaled(size / 1000000, decimals, scaledDecimals, 6, ' s'); + } +} + +function toMilliSeconds(size: number, decimals: number, scaledDecimals: number) { + if (size === null) { + return ''; + } + + if (Math.abs(size) < 1000) { + return toFixed(size, decimals) + ' ms'; + } else if (Math.abs(size) < 60000) { + // Less than 1 min + return toFixedScaled(size / 1000, decimals, scaledDecimals, 3, ' s'); + } else if (Math.abs(size) < 3600000) { + // Less than 1 hour, divide in minutes + return toFixedScaled(size / 60000, decimals, scaledDecimals, 5, ' min'); + } else if (Math.abs(size) < 86400000) { + // Less than one day, divide in hours + return toFixedScaled(size / 3600000, decimals, scaledDecimals, 7, ' hour'); + } else if (Math.abs(size) < 31536000000) { + // Less than one year, divide in days + return toFixedScaled(size / 86400000, decimals, scaledDecimals, 8, ' day'); + } + + return toFixedScaled(size / 31536000000, decimals, scaledDecimals, 10, ' year'); +} + +function toSeconds(size: number, decimals: number, scaledDecimals: number) { + if (size === null) { + return ''; + } + + // Less than 1 µs, divide in ns + if (Math.abs(size) < 0.000001) { + return toFixedScaled(size * 1e9, decimals, scaledDecimals - decimals, -9, ' ns'); + } + // Less than 1 ms, divide in µs + if (Math.abs(size) < 0.001) { + return toFixedScaled(size * 1e6, decimals, scaledDecimals - decimals, -6, ' µs'); + } + // Less than 1 second, divide in ms + if (Math.abs(size) < 1) { + return toFixedScaled(size * 1e3, decimals, scaledDecimals - decimals, -3, ' ms'); + } + + if (Math.abs(size) < 60) { + return toFixed(size, decimals) + ' s'; + } else if (Math.abs(size) < 3600) { + // Less than 1 hour, divide in minutes + return toFixedScaled(size / 60, decimals, scaledDecimals, 1, ' min'); + } else if (Math.abs(size) < 86400) { + // Less than one day, divide in hours + return toFixedScaled(size / 3600, decimals, scaledDecimals, 4, ' hour'); + } else if (Math.abs(size) < 604800) { + // Less than one week, divide in days + return toFixedScaled(size / 86400, decimals, scaledDecimals, 5, ' day'); + } else if (Math.abs(size) < 31536000) { + // Less than one year, divide in week + return toFixedScaled(size / 604800, decimals, scaledDecimals, 6, ' week'); + } + + return toFixedScaled(size / 3.15569e7, decimals, scaledDecimals, 7, ' year'); +} + +function toMinutes(size: number, decimals: number, scaledDecimals: number) { + if (size === null) { + return ''; + } + + if (Math.abs(size) < 60) { + return toFixed(size, decimals) + ' min'; + } else if (Math.abs(size) < 1440) { + return toFixedScaled(size / 60, decimals, scaledDecimals, 2, ' hour'); + } else if (Math.abs(size) < 10080) { + return toFixedScaled(size / 1440, decimals, scaledDecimals, 3, ' day'); + } else if (Math.abs(size) < 604800) { + return toFixedScaled(size / 10080, decimals, scaledDecimals, 4, ' week'); + } else { + return toFixedScaled(size / 5.25948e5, decimals, scaledDecimals, 5, ' year'); + } +} + +function toHours(size: number, decimals: number, scaledDecimals: number) { + if (size === null) { + return ''; + } + + if (Math.abs(size) < 24) { + return toFixed(size, decimals) + ' hour'; + } else if (Math.abs(size) < 168) { + return toFixedScaled(size / 24, decimals, scaledDecimals, 2, ' day'); + } else if (Math.abs(size) < 8760) { + return toFixedScaled(size / 168, decimals, scaledDecimals, 3, ' week'); + } else { + return toFixedScaled(size / 8760, decimals, scaledDecimals, 4, ' year'); + } +} + +function toDays(size: number, decimals: number, scaledDecimals: number) { + if (size === null) { + return ''; + } + + if (Math.abs(size) < 7) { + return toFixed(size, decimals) + ' day'; + } else if (Math.abs(size) < 365) { + return toFixedScaled(size / 7, decimals, scaledDecimals, 2, ' week'); + } else { + return toFixedScaled(size / 365, decimals, scaledDecimals, 3, ' year'); + } +} + +function toDuration(size: number, decimals: number, timeScale: Interval): string { + if (size === null) { + return ''; + } + if (size === 0) { + return '0 ' + timeScale + 's'; + } + if (size < 0) { + return toDuration(-size, decimals, timeScale) + ' ago'; + } + + const units = [ + { long: Interval.Year }, + { long: Interval.Month }, + { long: Interval.Week }, + { long: Interval.Day }, + { long: Interval.Hour }, + { long: Interval.Minute }, + { long: Interval.Second }, + { long: Interval.Millisecond }, + ]; + // convert $size to milliseconds + // intervals_in_seconds uses seconds (duh), convert them to milliseconds here to minimize floating point errors + size *= INTERVALS_IN_SECONDS[timeScale] * 1000; + + const strings = []; + // after first value >= 1 print only $decimals more + let decrementDecimals = false; + for (let i = 0; i < units.length && decimals >= 0; i++) { + const interval = INTERVALS_IN_SECONDS[units[i].long] * 1000; + const value = size / interval; + if (value >= 1 || decrementDecimals) { + decrementDecimals = true; + const floor = Math.floor(value); + const unit = units[i].long + (floor !== 1 ? 's' : ''); + strings.push(floor + ' ' + unit); + size = size % interval; + decimals--; + } + } + + return strings.join(', '); +} + +function toClock(size: number, decimals: number) { + if (size === null) { + return ''; + } + + // < 1 second + if (size < 1000) { + return moment.utc(size).format('SSS\\m\\s'); + } + + // < 1 minute + if (size < 60000) { + let format = 'ss\\s:SSS\\m\\s'; + if (decimals === 0) { + format = 'ss\\s'; + } + return moment.utc(size).format(format); + } + + // < 1 hour + if (size < 3600000) { + let format = 'mm\\m:ss\\s:SSS\\m\\s'; + if (decimals === 0) { + format = 'mm\\m'; + } else if (decimals === 1) { + format = 'mm\\m:ss\\s'; + } + return moment.utc(size).format(format); + } + + let format = 'mm\\m:ss\\s:SSS\\m\\s'; + + const hours = `${('0' + Math.floor(moment.duration(size, 'milliseconds').asHours())).slice(-2)}h`; + + if (decimals === 0) { + format = ''; + } else if (decimals === 1) { + format = 'mm\\m'; + } else if (decimals === 2) { + format = 'mm\\m:ss\\s'; + } + + return format ? `${hours}:${moment.utc(size).format(format)}` : hours; +} + +function toDurationInMilliseconds(size: number, decimals: number) { + return toDuration(size, decimals, Interval.Millisecond); +} + +function toDurationInSeconds(size: number, decimals: number) { + return toDuration(size, decimals, Interval.Second); +} + +function toDurationInHoursMinutesSeconds(size: number) { + const strings = []; + const numHours = Math.floor(size / 3600); + const numMinutes = Math.floor((size % 3600) / 60); + const numSeconds = Math.floor((size % 3600) % 60); + numHours > 9 ? strings.push('' + numHours) : strings.push('0' + numHours); + numMinutes > 9 ? strings.push('' + numMinutes) : strings.push('0' + numMinutes); + numSeconds > 9 ? strings.push('' + numSeconds) : strings.push('0' + numSeconds); + return strings.join(':'); +} + +function toTimeTicks(size: number, decimals: number, scaledDecimals: number) { + return toSeconds(size, decimals, scaledDecimals); +} + +function toClockMilliseconds(size: number, decimals: number) { + return toClock(size, decimals); +} + +function toClockSeconds(size: number, decimals: number) { + return toClock(size * 1000, decimals); +} + +function dateTimeAsIso(value: number, decimals: number, scaledDecimals: number, isUtc: boolean) { + const time = isUtc ? moment.utc(value) : moment(value); + + if (moment().isSame(value, 'day')) { + return time.format('HH:mm:ss'); + } + return time.format('YYYY-MM-DD HH:mm:ss'); +} + +function dateTimeAsUS(value: number, decimals: number, scaledDecimals: number, isUtc: boolean) { + const time = isUtc ? moment.utc(value) : moment(value); + + if (moment().isSame(value, 'day')) { + return time.format('h:mm:ss a'); + } + return time.format('MM/DD/YYYY h:mm:ss a'); +} + +function dateTimeFromNow(value: number, decimals: number, scaledDecimals: number, isUtc: boolean) { + const time = isUtc ? moment.utc(value) : moment(value); + return time.fromNow(); +} + +function binarySIPrefix(unit: string, offset = 0) { + const prefixes = ['', 'Ki', 'Mi', 'Gi', 'Ti', 'Pi', 'Ei', 'Zi', 'Yi'].slice(offset); + const units = prefixes.map(p => { + return ' ' + p + unit; + }); + return scaledUnits(1024, units); +} + +function decimalSIPrefix(unit: string, offset = 0) { + let prefixes = ['n', 'µ', 'm', '', 'k', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y']; + prefixes = prefixes.slice(3 + (offset || 0)); + const units = prefixes.map(p => { + return ' ' + p + unit; + }); + return scaledUnits(1000, units); +} + function buildFormats() { categories = [ { name: 'none', formats: [ - { - name: 'none', - id: 'none', - fn: toFixed, - }, + { name: 'none', id: 'none', fn: toFixed }, { name: 'short', id: 'short', fn: scaledUnits(1000, ['', ' K', ' Mil', ' Bil', ' Tri', ' Quadr', ' Quint', ' Sext', ' Sept']), }, - { - name: 'percent (0-100)', - id: 'percent', - fn: toPercent, - }, - { - name: 'percent (0.0-1.0)', - id: 'percentunit', - fn: toPercentUnit, - }, - { - name: 'Humidity (%H)', - id: 'humidity', - fn: toFixedUnit('%H'), - }, - { - name: 'decibel', - id: 'dB', - fn: toFixedUnit('dB'), - }, - { - name: 'hexadecimal (0x)', - id: 'hex0x', - fn: toHex0x, - }, - { - name: 'hexadecimal', - id: 'hex', - fn: hex, - }, - { - name: 'scientific notation', - id: 'sci', - fn: sci, - }, - { - name: 'locale format', - id: 'locale', - fn: locale, - }, + { name: 'percent (0-100)', id: 'percent', fn: toPercent }, + { name: 'percent (0.0-1.0)', id: 'percentunit', fn: toPercentUnit }, + { name: 'Humidity (%H)', id: 'humidity', fn: toFixedUnit('%H') }, + { name: 'decibel', id: 'dB', fn: toFixedUnit('dB') }, + { name: 'hexadecimal (0x)', id: 'hex0x', fn: toHex0x }, + { name: 'hexadecimal', id: 'hex', fn: hex }, + { name: 'scientific notation', id: 'sci', fn: sci }, + { name: 'locale format', id: 'locale', fn: locale }, + ], + }, + { + name: 'area', + formats: [ + { name: 'Square Meters (m²)', id: 'areaM2', fn: toFixedUnit('m²') }, + { name: 'Square Feet (ft²)', id: 'areaF2', fn: toFixedUnit('ft²') }, + { name: 'Square Miles (mi²)', id: 'areaMI2', fn: toFixedUnit('mi²') }, + ], + }, + { + name: 'computation throughput', + formats: [ + { name: 'FLOP/s', id: 'flops', fn: decimalSIPrefix('FLOP/s') }, + { name: 'MFLOP/s', id: 'mflops', fn: decimalSIPrefix('FLOP/s', 2) }, + { name: 'GFLOP/s', id: 'gflops', fn: decimalSIPrefix('FLOP/s', 3) }, + { name: 'TFLOP/s', id: 'tflops', fn: decimalSIPrefix('FLOP/s', 4) }, + { name: 'PFLOP/s', id: 'pflops', fn: decimalSIPrefix('FLOP/s', 5) }, + { name: 'EFLOP/s', id: 'eflops', fn: decimalSIPrefix('FLOP/s', 6) }, ], }, { @@ -221,6 +547,157 @@ function buildFormats() { { name: 'Bitcoin (฿)', id: 'currencyBTC', fn: currency('฿') }, ], }, + { + name: 'data (IEC)', + formats: [ + { name: 'bits', id: 'bits', fn: binarySIPrefix('b') }, + { name: 'bytes', id: 'bytes', fn: binarySIPrefix('B') }, + { name: 'kibibytes', id: 'kbytes', fn: binarySIPrefix('B', 1) }, + { name: 'mebibytes', id: 'mbytes', fn: binarySIPrefix('B', 2) }, + { name: 'gibibytes', id: 'gbytes', fn: binarySIPrefix('B', 3) }, + ], + }, + { + name: 'data (Metric)', + formats: [ + { name: 'bits', id: 'decbits', fn: decimalSIPrefix('d') }, + { name: 'bytes', id: 'decbytes', fn: decimalSIPrefix('B') }, + { name: 'kilobytes', id: 'deckbytes', fn: decimalSIPrefix('B', 1) }, + { name: 'megabytes', id: 'decmbytes', fn: decimalSIPrefix('B', 2) }, + { name: 'gigabytes', id: 'decgbytes', fn: decimalSIPrefix('B', 3) }, + ], + }, + { + name: 'data rate', + formats: [ + { name: 'packets/sec', id: 'pps', fn: decimalSIPrefix('pps') }, + { name: 'bits/sec', id: 'bps', fn: decimalSIPrefix('bps') }, + { name: 'bytes/sec', id: 'Bps', fn: decimalSIPrefix('B/s') }, + { name: 'kilobytes/sec', id: 'KBs', fn: decimalSIPrefix('Bs', 1) }, + { name: 'kilobits/sec', id: 'Kbits', fn: decimalSIPrefix('bps', 1) }, + { name: 'megabytes/sec', id: 'MBs', fn: decimalSIPrefix('Bs', 2) }, + { name: 'megabits/sec', id: 'Mbits', fn: decimalSIPrefix('bps', 2) }, + { name: 'gigabytes/sec', id: 'GBs', fn: decimalSIPrefix('Bs', 3) }, + { name: 'gigabits/sec', id: 'Gbits', fn: decimalSIPrefix('bps', 3) }, + ], + }, + { + name: 'date & time', + formats: [ + { name: 'YYYY-MM-DD HH:mm:ss', id: 'dateTimeAsIso', fn: dateTimeAsIso }, + { name: 'DD/MM/YYYY h:mm:ss a', id: 'dateTimeAsUS', fn: dateTimeAsUS }, + { name: 'From Now', id: 'dateTimeFromNow', fn: dateTimeFromNow }, + ], + }, + { + name: 'energy', + formats: [ + { name: 'Watt (W)', id: 'watt', fn: decimalSIPrefix('W') }, + { name: 'Kilowatt (kW)', id: 'kwatt', fn: decimalSIPrefix('W', 1) }, + { name: 'Milliwatt (mW)', id: 'mwatt', fn: decimalSIPrefix('W', -1) }, + { name: 'Watt per square meter (W/m²)', id: 'Wm2', fn: toFixedUnit('W/m²') }, + { name: 'Volt-ampere (VA)', id: 'voltamp', fn: decimalSIPrefix('VA') }, + { name: 'Kilovolt-ampere (kVA)', id: 'kvoltamp', fn: decimalSIPrefix('VA', 1) }, + { name: 'Volt-ampere reactive (var)', id: 'voltampreact', fn: decimalSIPrefix('var') }, + { name: 'Kilovolt-ampere reactive (kvar)', id: 'kvoltampreact', fn: decimalSIPrefix('var', 1) }, + { name: 'Watt-hour (Wh)', id: 'watth', fn: decimalSIPrefix('Wh') }, + { name: 'Kilowatt-hour (kWh)', id: 'kwatth', fn: decimalSIPrefix('Wh', 1) }, + { name: 'Kilowatt-min (kWm)', id: 'kwattm', fn: decimalSIPrefix('W/Min', 1) }, + { name: 'Joule (J)', id: 'joule', fn: decimalSIPrefix('J') }, + { name: 'Electron volt (eV)', id: 'ev', fn: decimalSIPrefix('eV') }, + { name: 'Ampere (A)', id: 'amp', fn: decimalSIPrefix('A') }, + { name: 'Kiloampere (kA)', id: 'kamp', fn: decimalSIPrefix('A', 1) }, + { name: 'Milliampere (mA)', id: 'mamp', fn: decimalSIPrefix('A', -1) }, + { name: 'Volt (V)', id: 'volt', fn: decimalSIPrefix('V') }, + { name: 'Kilovolt (kV)', id: 'kvolt', fn: decimalSIPrefix('V', 1) }, + { name: 'Millivolt (mV)', id: 'mvolt', fn: decimalSIPrefix('V', -1) }, + { name: 'Decibel-milliwatt (dBm)', id: 'dBm', fn: decimalSIPrefix('dBm') }, + { name: 'Ohm (Ω)', id: 'ohm', fn: decimalSIPrefix('Ω') }, + { name: 'Lumens (Lm)', id: 'lumens', fn: decimalSIPrefix('Lm') }, + ], + }, + { + name: 'hash rate', + formats: [ + { name: 'hashes/sec', id: 'Hs', fn: decimalSIPrefix('H/s') }, + { name: 'kilohashes/sec', id: 'KHs', fn: decimalSIPrefix('H/s', 1) }, + { name: 'megahashes/sec', id: 'MHs', fn: decimalSIPrefix('H/s', 2) }, + { name: 'gigahashes/sec', id: 'GHs', fn: decimalSIPrefix('H/s', 3) }, + { name: 'terahashes/sec', id: 'THs', fn: decimalSIPrefix('H/s', 4) }, + { name: 'petahashes/sec', id: 'PHs', fn: decimalSIPrefix('H/s', 5) }, + { name: 'exahashes/sec', id: 'EHs', fn: decimalSIPrefix('H/s', 6) }, + ], + }, + { + name: 'mass', + formats: [ + { name: 'milligram (mg)', id: 'massmg', fn: decimalSIPrefix('g', -1) }, + { name: 'gram (g)', id: 'massg', fn: decimalSIPrefix('g') }, + { name: 'kilogram (kg)', id: 'masskg', fn: decimalSIPrefix('g', 1) }, + { name: 'metric ton (t)', id: 'masst', fn: toFixedUnit('t') }, + ], + }, + { + name: 'length', + formats: [ + { name: 'millimetre (mm)', id: 'lengthmm', fn: decimalSIPrefix('m', -1) }, + { name: 'feet (ft)', id: 'lengthft', fn: toFixedUnit('ft') }, + { name: 'meter (m)', id: 'lengthm', fn: decimalSIPrefix('m') }, + { name: 'kilometer (km)', id: 'lengthkm', fn: decimalSIPrefix('m', 1) }, + { name: 'mile (mi)', id: 'lengthmi', fn: toFixedUnit('mi') }, + ], + }, + { + name: 'temperature', + formats: [ + { name: 'Celsius (°C)', id: 'celsius', fn: toFixedUnit('°C') }, + { name: 'Farenheit (°F)', id: 'farenheit', fn: toFixedUnit('°F') }, + { name: 'Kelvin (K)', id: 'kelvin', fn: toFixedUnit('K') }, + ], + }, + { + name: 'time', + formats: [ + { name: 'Hertz (1/s)', id: 'hertz', fn: decimalSIPrefix('Hz') }, + { name: 'nanoseconds (ns)', id: 'ns', fn: toNanoSeconds }, + { name: 'microseconds (µs)', id: 'µs', fn: toMicroSeconds }, + { name: 'milliseconds (ms)', id: 'ms', fn: toMilliSeconds }, + { name: 'seconds (s)', id: 's', fn: toSeconds }, + { name: 'minutes (m)', id: 'm', fn: toMinutes }, + { name: 'hours (h)', id: 'h', fn: toHours }, + { name: 'days (d)', id: 'd', fn: toDays }, + { name: 'duration (ms)', id: 'dtdurationms', fn: toDurationInMilliseconds }, + { name: 'duration (s)', id: 'dtdurations', fn: toDurationInSeconds }, + { name: 'duration (hh:mm:ss)', id: 'dthms', fn: toDurationInHoursMinutesSeconds }, + { name: 'Timeticks (s/100)', id: 'timeticks', fn: toTimeTicks }, + { name: 'clock (ms)', id: 'clockms', fn: toClockMilliseconds }, + { name: 'clock (s)', id: 'clocks', fn: toClockSeconds }, + ], + }, + { + name: 'throughput', + formats: [ + { name: 'ops/sec (ops)', id: 'ops', fn: decimalSIPrefix('ops') }, + { name: 'requests/sec (rps)', id: 'reqps', fn: decimalSIPrefix('reqps') }, + { name: 'reads/sec (rps)', id: 'rps', fn: decimalSIPrefix('rps') }, + { name: 'writes/sec (wps)', id: 'wps', fn: decimalSIPrefix('wps') }, + { name: 'I/O ops/sec (iops)', id: 'iops', fn: decimalSIPrefix('iops') }, + { name: 'ops/min (opm)', id: 'opm', fn: decimalSIPrefix('opm') }, + { name: 'reads/min (rpm)', id: 'rpm', fn: decimalSIPrefix('rpm') }, + { name: 'writes/min (wpm)', id: 'wpm', fn: decimalSIPrefix('wpm') }, + ], + }, + { + name: 'volume', + formats: [ + { name: 'millilitre (mL)', id: 'mlitre', fn: decimalSIPrefix('L', -1) }, + { name: 'litre (L)', id: 'litre', fn: decimalSIPrefix('L') }, + { name: 'cubic metre', id: 'm3', fn: toFixedUnit('m³') }, + { name: 'Normal cubic metre', id: 'Nm3', fn: toFixedUnit('Nm³') }, + { name: 'cubic decimetre', id: 'dm3', fn: toFixedUnit('dm³') }, + { name: 'gallons', id: 'gallons', fn: toFixedUnit('gal') }, + ], + }, ]; for (const cat of categories) { @@ -259,7 +736,7 @@ export function getUnitFormats() { submenu: cat.formats.map(format => { return { text: format.name, - value: format.id, + id: format.id, }; }), }; From abb98a25e4962c1af80303c5e94579aebf39f1c3 Mon Sep 17 00:00:00 2001 From: Peter Holmberg Date: Tue, 8 Jan 2019 10:35:10 +0100 Subject: [PATCH 04/26] moved all units --- packages/grafana-ui/src/utils/valueFormats.ts | 80 +++++++++++++++++++ 1 file changed, 80 insertions(+) diff --git a/packages/grafana-ui/src/utils/valueFormats.ts b/packages/grafana-ui/src/utils/valueFormats.ts index c7c8dbd447b..5e86a583bb4 100644 --- a/packages/grafana-ui/src/utils/valueFormats.ts +++ b/packages/grafana-ui/src/utils/valueFormats.ts @@ -508,6 +508,22 @@ function buildFormats() { { name: 'locale format', id: 'locale', fn: locale }, ], }, + { + name: 'acceleration', + formats: [ + { name: 'Meters/sec²', id: 'accMS2', fn: toFixedUnit('m/sec²') }, + { name: 'Feet/sec²', id: 'accFS2', fn: toFixedUnit('f/sec²') }, + { name: 'G unit', id: 'accG', fn: toFixedUnit('g') }, + ], + }, + { + name: 'angle', + formats: [ + { name: 'Degrees (°)', id: 'degree', fn: toFixedUnit('°') }, + { name: 'Radians', id: 'radian', fn: toFixedUnit('rad') }, + { name: 'Gradian', id: 'grad', fn: toFixedUnit('grad') }, + ], + }, { name: 'area', formats: [ @@ -527,6 +543,23 @@ function buildFormats() { { name: 'EFLOP/s', id: 'eflops', fn: decimalSIPrefix('FLOP/s', 6) }, ], }, + { + name: 'concentration', + formats: [ + { name: 'parts-per-million (ppm)', id: 'ppm', fn: toFixedUnit('ppm') }, + { name: 'parts-per-billion (ppb)', id: 'conppb', fn: toFixedUnit('ppb') }, + { name: 'nanogram per cubic meter (ng/m³)', id: 'conngm3', fn: toFixedUnit('ng/m³') }, + { name: 'nanogram per normal cubic meter (ng/Nm³)', id: 'conngNm3', fn: toFixedUnit('ng/Nm³') }, + { name: 'microgram per cubic meter (μg/m³)', id: 'conμgm3', fn: toFixedUnit('μg/m³') }, + { name: 'microgram per normal cubic meter (μg/Nm³)', id: 'conμgNm3', fn: toFixedUnit('μg/Nm³') }, + { name: 'milligram per cubic meter (mg/m³)', id: 'conmgm3', fn: toFixedUnit('mg/m³') }, + { name: 'milligram per normal cubic meter (mg/Nm³)', id: 'conmgNm3', fn: toFixedUnit('mg/Nm³') }, + { name: 'gram per cubic meter (g/m³)', id: 'congm3', fn: toFixedUnit('g/m³') }, + { name: 'gram per normal cubic meter (g/Nm³)', id: 'congNm3', fn: toFixedUnit('g/Nm³') }, + { name: 'milligrams per decilitre (mg/dL)', id: 'conmgdL', fn: toFixedUnit('mg/dL') }, + { name: 'millimoles per litre (mmol/L)', id: 'conmmolL', fn: toFixedUnit('mmol/L') }, + ], + }, { name: 'currency', formats: [ @@ -616,6 +649,27 @@ function buildFormats() { { name: 'Lumens (Lm)', id: 'lumens', fn: decimalSIPrefix('Lm') }, ], }, + { + name: 'flow', + formats: [ + { name: 'Gallons/min (gpm)', id: 'flowgpm', fn: toFixedUnit('gpm') }, + { name: 'Cubic meters/sec (cms)', id: 'flowcms', fn: toFixedUnit('cms') }, + { name: 'Cubic feet/sec (cfs)', id: 'flowcfs', fn: toFixedUnit('cfs') }, + { name: 'Cubic feet/min (cfm)', id: 'flowcfm', fn: toFixedUnit('cfm') }, + { name: 'Litre/hour', id: 'litreh', fn: toFixedUnit('l/h') }, + { name: 'Litre/min (l/min)', id: 'flowlpm', fn: toFixedUnit('l/min') }, + { name: 'milliLitre/min (mL/min)', id: 'flowmlpm', fn: toFixedUnit('mL/min') }, + ], + }, + { + name: 'force', + formats: [ + { name: 'Newton-meters (Nm)', id: 'forceNm', fn: decimalSIPrefix('Nm') }, + { name: 'Kilonewton-meters (kNm)', id: 'forcekNm', fn: decimalSIPrefix('Nm', 1) }, + { name: 'Newtons (N)', id: 'forceN', fn: decimalSIPrefix('N') }, + { name: 'Kilonewtons (kN)', id: 'forcekN', fn: decimalSIPrefix('N', 1) }, + ], + }, { name: 'hash rate', formats: [ @@ -647,6 +701,32 @@ function buildFormats() { { name: 'mile (mi)', id: 'lengthmi', fn: toFixedUnit('mi') }, ], }, + { + name: 'pressure', + formats: [ + { name: 'Millibars', id: 'pressurembar', fn: decimalSIPrefix('bar', -1) }, + { name: 'Bars', id: 'pressurebar', fn: decimalSIPrefix('bar') }, + { name: 'Kilobars', id: 'pressurekbar', fn: decimalSIPrefix('bar', 1) }, + { name: 'Hectopascals', id: 'pressurehpa', fn: toFixedUnit('hPa') }, + { name: 'Kilopascals', id: 'pressurekpa', fn: toFixedUnit('kPa') }, + { name: 'Inches of mercury', id: 'pressurehg', fn: toFixedUnit('"Hg') }, + { name: 'PSI', id: 'pressurepsi', fn: scaledUnits(1000, ['psi', 'ksi', 'Mpsi']) }, + ], + }, + { + name: 'radiation', + formats: [ + { name: 'Becquerel (Bq)', id: 'radbq', fn: decimalSIPrefix('Bq') }, + { name: 'curie (Ci)', id: 'radci', fn: decimalSIPrefix('Ci') }, + { name: 'Gray (Gy)', id: 'radgy', fn: decimalSIPrefix('Gy') }, + { name: 'rad', id: 'radrad', fn: decimalSIPrefix('rad') }, + { name: 'Sievert (Sv)', id: 'radsv', fn: decimalSIPrefix('Sv') }, + { name: 'rem', id: 'radrem', fn: decimalSIPrefix('rem') }, + { name: 'Exposure (C/kg)', id: 'radexpckg', fn: decimalSIPrefix('C/kg') }, + { name: 'roentgen (R)', id: 'radr', fn: decimalSIPrefix('R') }, + { name: 'Sievert/hour (Sv/h)', id: 'radsvh', fn: decimalSIPrefix('Sv/h') }, + ], + }, { name: 'temperature', formats: [ From de3876997311b984f68add424893e5a2988a3181 Mon Sep 17 00:00:00 2001 From: Peter Holmberg Date: Tue, 8 Jan 2019 15:25:23 +0100 Subject: [PATCH 05/26] splitting into more files --- .../ValueFormats/arithmeticFormatters.test.ts | 40 + .../ValueFormats/arithmeticFormatters.ts | 42 + .../src/utils/ValueFormats/categories.ts | 313 +++++++ .../ValueFormats/dateTimeFormatters.test.ts | 231 +++++ .../utils/ValueFormats/dateTimeFormatters.ts | 312 +++++++ .../ValueFormats/symbolFormatters.test.ts | 11 + .../utils/ValueFormats/symbolFormatters.ts | 30 + .../src/utils/ValueFormats/valueFormats.ts | 154 ++++ packages/grafana-ui/src/utils/index.ts | 2 +- packages/grafana-ui/src/utils/valueFormats.ts | 824 ------------------ 10 files changed, 1134 insertions(+), 825 deletions(-) create mode 100644 packages/grafana-ui/src/utils/ValueFormats/arithmeticFormatters.test.ts create mode 100644 packages/grafana-ui/src/utils/ValueFormats/arithmeticFormatters.ts create mode 100644 packages/grafana-ui/src/utils/ValueFormats/categories.ts create mode 100644 packages/grafana-ui/src/utils/ValueFormats/dateTimeFormatters.test.ts create mode 100644 packages/grafana-ui/src/utils/ValueFormats/dateTimeFormatters.ts create mode 100644 packages/grafana-ui/src/utils/ValueFormats/symbolFormatters.test.ts create mode 100644 packages/grafana-ui/src/utils/ValueFormats/symbolFormatters.ts create mode 100644 packages/grafana-ui/src/utils/ValueFormats/valueFormats.ts delete mode 100644 packages/grafana-ui/src/utils/valueFormats.ts diff --git a/packages/grafana-ui/src/utils/ValueFormats/arithmeticFormatters.test.ts b/packages/grafana-ui/src/utils/ValueFormats/arithmeticFormatters.test.ts new file mode 100644 index 00000000000..44332a51307 --- /dev/null +++ b/packages/grafana-ui/src/utils/ValueFormats/arithmeticFormatters.test.ts @@ -0,0 +1,40 @@ +import { toHex, toHex0x } from './arithmeticFormatters'; + +describe('hex', () => { + it('positive integer', () => { + const str = toHex(100, 0); + expect(str).toBe('64'); + }); + it('negative integer', () => { + const str = toHex(-100, 0); + expect(str).toBe('-64'); + }); + it('positive float', () => { + const str = toHex(50.52, 1); + expect(str).toBe('32.8'); + }); + it('negative float', () => { + const str = toHex(-50.333, 2); + expect(str).toBe('-32.547AE147AE14'); + }); +}); + +describe('hex 0x', () => { + it('positive integeter', () => { + const str = toHex0x(7999, 0); + expect(str).toBe('0x1F3F'); + }); + it('negative integer', () => { + const str = toHex0x(-584, 0); + expect(str).toBe('-0x248'); + }); + + it('positive float', () => { + const str = toHex0x(74.443, 3); + expect(str).toBe('0x4A.716872B020C4'); + }); + it('negative float', () => { + const str = toHex0x(-65.458, 1); + expect(str).toBe('-0x41.8'); + }); +}); diff --git a/packages/grafana-ui/src/utils/ValueFormats/arithmeticFormatters.ts b/packages/grafana-ui/src/utils/ValueFormats/arithmeticFormatters.ts new file mode 100644 index 00000000000..fa9daf0fb97 --- /dev/null +++ b/packages/grafana-ui/src/utils/ValueFormats/arithmeticFormatters.ts @@ -0,0 +1,42 @@ +import { toFixed } from './valueFormats'; + +export function toPercent(size: number, decimals: number) { + if (size === null) { + return ''; + } + return toFixed(size, decimals) + '%'; +} + +export function toPercentUnit(size: number, decimals: number) { + if (size === null) { + return ''; + } + return toFixed(100 * size, decimals) + '%'; +} + +export function toHex0x(value: number, decimals: number) { + if (value == null) { + return ''; + } + const hexString = toHex(value, decimals); + if (hexString.substring(0, 1) === '-') { + return '-0x' + hexString.substring(1); + } + return '0x' + hexString; +} + +export function toHex(value: number, decimals: number) { + if (value == null) { + return ''; + } + return parseFloat(toFixed(value, decimals)) + .toString(16) + .toUpperCase(); +} + +export function sci(value: number, decimals: number) { + if (value == null) { + return ''; + } + return value.toExponential(decimals); +} diff --git a/packages/grafana-ui/src/utils/ValueFormats/categories.ts b/packages/grafana-ui/src/utils/ValueFormats/categories.ts new file mode 100644 index 00000000000..15ea8d15468 --- /dev/null +++ b/packages/grafana-ui/src/utils/ValueFormats/categories.ts @@ -0,0 +1,313 @@ +import { locale, scaledUnits, toFixed, toFixedUnit, ValueFormatCategory } from './valueFormats'; +import { + dateTimeAsIso, + dateTimeAsUS, + dateTimeFromNow, + toClockMilliseconds, + toClockSeconds, + toDays, + toDurationInHoursMinutesSeconds, + toDurationInMilliseconds, + toDurationInSeconds, + toHours, + toMicroSeconds, + toMilliSeconds, + toMinutes, + toNanoSeconds, + toSeconds, + toTimeTicks, +} from './dateTimeFormatters'; +import { toHex, sci, toHex0x, toPercent, toPercentUnit } from './arithmeticFormatters'; +import { binarySIPrefix, currency, decimalSIPrefix } from './symbolFormatters'; + +export const getCategories = (): ValueFormatCategory[] => [ + { + name: 'none', + formats: [ + { name: 'none', id: 'none', fn: toFixed }, + { + name: 'short', + id: 'short', + fn: scaledUnits(1000, ['', ' K', ' Mil', ' Bil', ' Tri', ' Quadr', ' Quint', ' Sext', ' Sept']), + }, + { name: 'percent (0-100)', id: 'percent', fn: toPercent }, + { name: 'percent (0.0-1.0)', id: 'percentunit', fn: toPercentUnit }, + { name: 'Humidity (%H)', id: 'humidity', fn: toFixedUnit('%H') }, + { name: 'decibel', id: 'dB', fn: toFixedUnit('dB') }, + { name: 'hexadecimal (0x)', id: 'hex0x', fn: toHex0x }, + { name: 'hexadecimal', id: 'hex', fn: toHex }, + { name: 'scientific notation', id: 'sci', fn: sci }, + { name: 'locale format', id: 'locale', fn: locale }, + ], + }, + { + name: 'acceleration', + formats: [ + { name: 'Meters/sec²', id: 'accMS2', fn: toFixedUnit('m/sec²') }, + { name: 'Feet/sec²', id: 'accFS2', fn: toFixedUnit('f/sec²') }, + { name: 'G unit', id: 'accG', fn: toFixedUnit('g') }, + ], + }, + { + name: 'angle', + formats: [ + { name: 'Degrees (°)', id: 'degree', fn: toFixedUnit('°') }, + { name: 'Radians', id: 'radian', fn: toFixedUnit('rad') }, + { name: 'Gradian', id: 'grad', fn: toFixedUnit('grad') }, + ], + }, + { + name: 'area', + formats: [ + { name: 'Square Meters (m²)', id: 'areaM2', fn: toFixedUnit('m²') }, + { name: 'Square Feet (ft²)', id: 'areaF2', fn: toFixedUnit('ft²') }, + { name: 'Square Miles (mi²)', id: 'areaMI2', fn: toFixedUnit('mi²') }, + ], + }, + { + name: 'computation throughput', + formats: [ + { name: 'FLOP/s', id: 'flops', fn: decimalSIPrefix('FLOP/s') }, + { name: 'MFLOP/s', id: 'mflops', fn: decimalSIPrefix('FLOP/s', 2) }, + { name: 'GFLOP/s', id: 'gflops', fn: decimalSIPrefix('FLOP/s', 3) }, + { name: 'TFLOP/s', id: 'tflops', fn: decimalSIPrefix('FLOP/s', 4) }, + { name: 'PFLOP/s', id: 'pflops', fn: decimalSIPrefix('FLOP/s', 5) }, + { name: 'EFLOP/s', id: 'eflops', fn: decimalSIPrefix('FLOP/s', 6) }, + ], + }, + { + name: 'concentration', + formats: [ + { name: 'parts-per-million (ppm)', id: 'ppm', fn: toFixedUnit('ppm') }, + { name: 'parts-per-billion (ppb)', id: 'conppb', fn: toFixedUnit('ppb') }, + { name: 'nanogram per cubic meter (ng/m³)', id: 'conngm3', fn: toFixedUnit('ng/m³') }, + { name: 'nanogram per normal cubic meter (ng/Nm³)', id: 'conngNm3', fn: toFixedUnit('ng/Nm³') }, + { name: 'microgram per cubic meter (μg/m³)', id: 'conμgm3', fn: toFixedUnit('μg/m³') }, + { name: 'microgram per normal cubic meter (μg/Nm³)', id: 'conμgNm3', fn: toFixedUnit('μg/Nm³') }, + { name: 'milligram per cubic meter (mg/m³)', id: 'conmgm3', fn: toFixedUnit('mg/m³') }, + { name: 'milligram per normal cubic meter (mg/Nm³)', id: 'conmgNm3', fn: toFixedUnit('mg/Nm³') }, + { name: 'gram per cubic meter (g/m³)', id: 'congm3', fn: toFixedUnit('g/m³') }, + { name: 'gram per normal cubic meter (g/Nm³)', id: 'congNm3', fn: toFixedUnit('g/Nm³') }, + { name: 'milligrams per decilitre (mg/dL)', id: 'conmgdL', fn: toFixedUnit('mg/dL') }, + { name: 'millimoles per litre (mmol/L)', id: 'conmmolL', fn: toFixedUnit('mmol/L') }, + ], + }, + { + name: 'currency', + formats: [ + { name: 'Dollars ($)', id: 'currencyUSD', fn: currency('$') }, + { name: 'Pounds (£)', id: 'currencyGBP', fn: currency('£') }, + { name: 'Euro (€)', id: 'currencyEUR', fn: currency('€') }, + { name: 'Yen (¥)', id: 'currencyJPY', fn: currency('¥') }, + { name: 'Rubles (₽)', id: 'currencyRUB', fn: currency('₽') }, + { name: 'Hryvnias (₴)', id: 'currencyUAH', fn: currency('₴') }, + { name: 'Real (R$)', id: 'currencyBRL', fn: currency('R$') }, + { name: 'Danish Krone (kr)', id: 'currencyDKK', fn: currency('kr') }, + { name: 'Icelandic Króna (kr)', id: 'currencyISK', fn: currency('kr') }, + { name: 'Norwegian Krone (kr)', id: 'currencyNOK', fn: currency('kr') }, + { name: 'Swedish Krona (kr)', id: 'currencySEK', fn: currency('kr') }, + { name: 'Czech koruna (czk)', id: 'currencyCZK', fn: currency('czk') }, + { name: 'Swiss franc (CHF)', id: 'currencyCHF', fn: currency('CHF') }, + { name: 'Polish Złoty (PLN)', id: 'currencyPLN', fn: currency('PLN') }, + { name: 'Bitcoin (฿)', id: 'currencyBTC', fn: currency('฿') }, + ], + }, + { + name: 'data (IEC)', + formats: [ + { name: 'bits', id: 'bits', fn: binarySIPrefix('b') }, + { name: 'bytes', id: 'bytes', fn: binarySIPrefix('B') }, + { name: 'kibibytes', id: 'kbytes', fn: binarySIPrefix('B', 1) }, + { name: 'mebibytes', id: 'mbytes', fn: binarySIPrefix('B', 2) }, + { name: 'gibibytes', id: 'gbytes', fn: binarySIPrefix('B', 3) }, + ], + }, + { + name: 'data (Metric)', + formats: [ + { name: 'bits', id: 'decbits', fn: decimalSIPrefix('d') }, + { name: 'bytes', id: 'decbytes', fn: decimalSIPrefix('B') }, + { name: 'kilobytes', id: 'deckbytes', fn: decimalSIPrefix('B', 1) }, + { name: 'megabytes', id: 'decmbytes', fn: decimalSIPrefix('B', 2) }, + { name: 'gigabytes', id: 'decgbytes', fn: decimalSIPrefix('B', 3) }, + ], + }, + { + name: 'data rate', + formats: [ + { name: 'packets/sec', id: 'pps', fn: decimalSIPrefix('pps') }, + { name: 'bits/sec', id: 'bps', fn: decimalSIPrefix('bps') }, + { name: 'bytes/sec', id: 'Bps', fn: decimalSIPrefix('B/s') }, + { name: 'kilobytes/sec', id: 'KBs', fn: decimalSIPrefix('Bs', 1) }, + { name: 'kilobits/sec', id: 'Kbits', fn: decimalSIPrefix('bps', 1) }, + { name: 'megabytes/sec', id: 'MBs', fn: decimalSIPrefix('Bs', 2) }, + { name: 'megabits/sec', id: 'Mbits', fn: decimalSIPrefix('bps', 2) }, + { name: 'gigabytes/sec', id: 'GBs', fn: decimalSIPrefix('Bs', 3) }, + { name: 'gigabits/sec', id: 'Gbits', fn: decimalSIPrefix('bps', 3) }, + ], + }, + { + name: 'date & time', + formats: [ + { name: 'YYYY-MM-DD HH:mm:ss', id: 'dateTimeAsIso', fn: dateTimeAsIso }, + { name: 'DD/MM/YYYY h:mm:ss a', id: 'dateTimeAsUS', fn: dateTimeAsUS }, + { name: 'From Now', id: 'dateTimeFromNow', fn: dateTimeFromNow }, + ], + }, + { + name: 'energy', + formats: [ + { name: 'Watt (W)', id: 'watt', fn: decimalSIPrefix('W') }, + { name: 'Kilowatt (kW)', id: 'kwatt', fn: decimalSIPrefix('W', 1) }, + { name: 'Milliwatt (mW)', id: 'mwatt', fn: decimalSIPrefix('W', -1) }, + { name: 'Watt per square meter (W/m²)', id: 'Wm2', fn: toFixedUnit('W/m²') }, + { name: 'Volt-ampere (VA)', id: 'voltamp', fn: decimalSIPrefix('VA') }, + { name: 'Kilovolt-ampere (kVA)', id: 'kvoltamp', fn: decimalSIPrefix('VA', 1) }, + { name: 'Volt-ampere reactive (var)', id: 'voltampreact', fn: decimalSIPrefix('var') }, + { name: 'Kilovolt-ampere reactive (kvar)', id: 'kvoltampreact', fn: decimalSIPrefix('var', 1) }, + { name: 'Watt-hour (Wh)', id: 'watth', fn: decimalSIPrefix('Wh') }, + { name: 'Kilowatt-hour (kWh)', id: 'kwatth', fn: decimalSIPrefix('Wh', 1) }, + { name: 'Kilowatt-min (kWm)', id: 'kwattm', fn: decimalSIPrefix('W/Min', 1) }, + { name: 'Joule (J)', id: 'joule', fn: decimalSIPrefix('J') }, + { name: 'Electron volt (eV)', id: 'ev', fn: decimalSIPrefix('eV') }, + { name: 'Ampere (A)', id: 'amp', fn: decimalSIPrefix('A') }, + { name: 'Kiloampere (kA)', id: 'kamp', fn: decimalSIPrefix('A', 1) }, + { name: 'Milliampere (mA)', id: 'mamp', fn: decimalSIPrefix('A', -1) }, + { name: 'Volt (V)', id: 'volt', fn: decimalSIPrefix('V') }, + { name: 'Kilovolt (kV)', id: 'kvolt', fn: decimalSIPrefix('V', 1) }, + { name: 'Millivolt (mV)', id: 'mvolt', fn: decimalSIPrefix('V', -1) }, + { name: 'Decibel-milliwatt (dBm)', id: 'dBm', fn: decimalSIPrefix('dBm') }, + { name: 'Ohm (Ω)', id: 'ohm', fn: decimalSIPrefix('Ω') }, + { name: 'Lumens (Lm)', id: 'lumens', fn: decimalSIPrefix('Lm') }, + ], + }, + { + name: 'flow', + formats: [ + { name: 'Gallons/min (gpm)', id: 'flowgpm', fn: toFixedUnit('gpm') }, + { name: 'Cubic meters/sec (cms)', id: 'flowcms', fn: toFixedUnit('cms') }, + { name: 'Cubic feet/sec (cfs)', id: 'flowcfs', fn: toFixedUnit('cfs') }, + { name: 'Cubic feet/min (cfm)', id: 'flowcfm', fn: toFixedUnit('cfm') }, + { name: 'Litre/hour', id: 'litreh', fn: toFixedUnit('l/h') }, + { name: 'Litre/min (l/min)', id: 'flowlpm', fn: toFixedUnit('l/min') }, + { name: 'milliLitre/min (mL/min)', id: 'flowmlpm', fn: toFixedUnit('mL/min') }, + ], + }, + { + name: 'force', + formats: [ + { name: 'Newton-meters (Nm)', id: 'forceNm', fn: decimalSIPrefix('Nm') }, + { name: 'Kilonewton-meters (kNm)', id: 'forcekNm', fn: decimalSIPrefix('Nm', 1) }, + { name: 'Newtons (N)', id: 'forceN', fn: decimalSIPrefix('N') }, + { name: 'Kilonewtons (kN)', id: 'forcekN', fn: decimalSIPrefix('N', 1) }, + ], + }, + { + name: 'hash rate', + formats: [ + { name: 'hashes/sec', id: 'Hs', fn: decimalSIPrefix('H/s') }, + { name: 'kilohashes/sec', id: 'KHs', fn: decimalSIPrefix('H/s', 1) }, + { name: 'megahashes/sec', id: 'MHs', fn: decimalSIPrefix('H/s', 2) }, + { name: 'gigahashes/sec', id: 'GHs', fn: decimalSIPrefix('H/s', 3) }, + { name: 'terahashes/sec', id: 'THs', fn: decimalSIPrefix('H/s', 4) }, + { name: 'petahashes/sec', id: 'PHs', fn: decimalSIPrefix('H/s', 5) }, + { name: 'exahashes/sec', id: 'EHs', fn: decimalSIPrefix('H/s', 6) }, + ], + }, + { + name: 'mass', + formats: [ + { name: 'milligram (mg)', id: 'massmg', fn: decimalSIPrefix('g', -1) }, + { name: 'gram (g)', id: 'massg', fn: decimalSIPrefix('g') }, + { name: 'kilogram (kg)', id: 'masskg', fn: decimalSIPrefix('g', 1) }, + { name: 'metric ton (t)', id: 'masst', fn: toFixedUnit('t') }, + ], + }, + { + name: 'length', + formats: [ + { name: 'millimetre (mm)', id: 'lengthmm', fn: decimalSIPrefix('m', -1) }, + { name: 'feet (ft)', id: 'lengthft', fn: toFixedUnit('ft') }, + { name: 'meter (m)', id: 'lengthm', fn: decimalSIPrefix('m') }, + { name: 'kilometer (km)', id: 'lengthkm', fn: decimalSIPrefix('m', 1) }, + { name: 'mile (mi)', id: 'lengthmi', fn: toFixedUnit('mi') }, + ], + }, + { + name: 'pressure', + formats: [ + { name: 'Millibars', id: 'pressurembar', fn: decimalSIPrefix('bar', -1) }, + { name: 'Bars', id: 'pressurebar', fn: decimalSIPrefix('bar') }, + { name: 'Kilobars', id: 'pressurekbar', fn: decimalSIPrefix('bar', 1) }, + { name: 'Hectopascals', id: 'pressurehpa', fn: toFixedUnit('hPa') }, + { name: 'Kilopascals', id: 'pressurekpa', fn: toFixedUnit('kPa') }, + { name: 'Inches of mercury', id: 'pressurehg', fn: toFixedUnit('"Hg') }, + { name: 'PSI', id: 'pressurepsi', fn: scaledUnits(1000, ['psi', 'ksi', 'Mpsi']) }, + ], + }, + { + name: 'radiation', + formats: [ + { name: 'Becquerel (Bq)', id: 'radbq', fn: decimalSIPrefix('Bq') }, + { name: 'curie (Ci)', id: 'radci', fn: decimalSIPrefix('Ci') }, + { name: 'Gray (Gy)', id: 'radgy', fn: decimalSIPrefix('Gy') }, + { name: 'rad', id: 'radrad', fn: decimalSIPrefix('rad') }, + { name: 'Sievert (Sv)', id: 'radsv', fn: decimalSIPrefix('Sv') }, + { name: 'rem', id: 'radrem', fn: decimalSIPrefix('rem') }, + { name: 'Exposure (C/kg)', id: 'radexpckg', fn: decimalSIPrefix('C/kg') }, + { name: 'roentgen (R)', id: 'radr', fn: decimalSIPrefix('R') }, + { name: 'Sievert/hour (Sv/h)', id: 'radsvh', fn: decimalSIPrefix('Sv/h') }, + ], + }, + { + name: 'temperature', + formats: [ + { name: 'Celsius (°C)', id: 'celsius', fn: toFixedUnit('°C') }, + { name: 'Farenheit (°F)', id: 'farenheit', fn: toFixedUnit('°F') }, + { name: 'Kelvin (K)', id: 'kelvin', fn: toFixedUnit('K') }, + ], + }, + { + name: 'time', + formats: [ + { name: 'Hertz (1/s)', id: 'hertz', fn: decimalSIPrefix('Hz') }, + { name: 'nanoseconds (ns)', id: 'ns', fn: toNanoSeconds }, + { name: 'microseconds (µs)', id: 'µs', fn: toMicroSeconds }, + { name: 'milliseconds (ms)', id: 'ms', fn: toMilliSeconds }, + { name: 'seconds (s)', id: 's', fn: toSeconds }, + { name: 'minutes (m)', id: 'm', fn: toMinutes }, + { name: 'hours (h)', id: 'h', fn: toHours }, + { name: 'days (d)', id: 'd', fn: toDays }, + { name: 'duration (ms)', id: 'dtdurationms', fn: toDurationInMilliseconds }, + { name: 'duration (s)', id: 'dtdurations', fn: toDurationInSeconds }, + { name: 'duration (hh:mm:ss)', id: 'dthms', fn: toDurationInHoursMinutesSeconds }, + { name: 'Timeticks (s/100)', id: 'timeticks', fn: toTimeTicks }, + { name: 'clock (ms)', id: 'clockms', fn: toClockMilliseconds }, + { name: 'clock (s)', id: 'clocks', fn: toClockSeconds }, + ], + }, + { + name: 'throughput', + formats: [ + { name: 'ops/sec (ops)', id: 'ops', fn: decimalSIPrefix('ops') }, + { name: 'requests/sec (rps)', id: 'reqps', fn: decimalSIPrefix('reqps') }, + { name: 'reads/sec (rps)', id: 'rps', fn: decimalSIPrefix('rps') }, + { name: 'writes/sec (wps)', id: 'wps', fn: decimalSIPrefix('wps') }, + { name: 'I/O ops/sec (iops)', id: 'iops', fn: decimalSIPrefix('iops') }, + { name: 'ops/min (opm)', id: 'opm', fn: decimalSIPrefix('opm') }, + { name: 'reads/min (rpm)', id: 'rpm', fn: decimalSIPrefix('rpm') }, + { name: 'writes/min (wpm)', id: 'wpm', fn: decimalSIPrefix('wpm') }, + ], + }, + { + name: 'volume', + formats: [ + { name: 'millilitre (mL)', id: 'mlitre', fn: decimalSIPrefix('L', -1) }, + { name: 'litre (L)', id: 'litre', fn: decimalSIPrefix('L') }, + { name: 'cubic metre', id: 'm3', fn: toFixedUnit('m³') }, + { name: 'Normal cubic metre', id: 'Nm3', fn: toFixedUnit('Nm³') }, + { name: 'cubic decimetre', id: 'dm3', fn: toFixedUnit('dm³') }, + { name: 'gallons', id: 'gallons', fn: toFixedUnit('gal') }, + ], + }, +]; diff --git a/packages/grafana-ui/src/utils/ValueFormats/dateTimeFormatters.test.ts b/packages/grafana-ui/src/utils/ValueFormats/dateTimeFormatters.test.ts new file mode 100644 index 00000000000..cf69a1d433a --- /dev/null +++ b/packages/grafana-ui/src/utils/ValueFormats/dateTimeFormatters.test.ts @@ -0,0 +1,231 @@ +import moment from 'moment'; +import { + dateTimeAsIso, + dateTimeAsUS, + dateTimeFromNow, + Interval, + toClock, + toDuration, + toDurationInMilliseconds, + toDurationInSeconds, +} from './dateTimeFormatters'; + +describe('date time formats', () => { + const epoch = 1505634997920; + const utcTime = moment.utc(epoch); + const browserTime = moment(epoch); + + it('should format as iso date', () => { + const expected = browserTime.format('YYYY-MM-DD HH:mm:ss'); + const actual = dateTimeAsIso(epoch, 0, 0, false); + expect(actual).toBe(expected); + }); + + it('should format as iso date (in UTC)', () => { + const expected = utcTime.format('YYYY-MM-DD HH:mm:ss'); + const actual = dateTimeAsIso(epoch, 0, 0, true); + expect(actual).toBe(expected); + }); + + it('should format as iso date and skip date when today', () => { + const now = moment(); + const expected = now.format('HH:mm:ss'); + const actual = dateTimeAsIso(now.valueOf(), 0, 0, false); + expect(actual).toBe(expected); + }); + + it('should format as iso date (in UTC) and skip date when today', () => { + const now = moment.utc(); + const expected = now.format('HH:mm:ss'); + const actual = dateTimeAsIso(now.valueOf(), 0, 0, true); + expect(actual).toBe(expected); + }); + + it('should format as US date', () => { + const expected = browserTime.format('MM/DD/YYYY h:mm:ss a'); + const actual = dateTimeAsUS(epoch, 0, 0, false); + expect(actual).toBe(expected); + }); + + it('should format as US date (in UTC)', () => { + const expected = utcTime.format('MM/DD/YYYY h:mm:ss a'); + const actual = dateTimeAsUS(epoch, 0, 0, true); + expect(actual).toBe(expected); + }); + + it('should format as US date and skip date when today', () => { + const now = moment(); + const expected = now.format('h:mm:ss a'); + const actual = dateTimeAsUS(now.valueOf(), 0, 0, false); + expect(actual).toBe(expected); + }); + + it('should format as US date (in UTC) and skip date when today', () => { + const now = moment.utc(); + const expected = now.format('h:mm:ss a'); + const actual = dateTimeAsUS(now.valueOf(), 0, 0, true); + expect(actual).toBe(expected); + }); + + it('should format as from now with days', () => { + const daysAgo = moment().add(-7, 'd'); + const expected = '7 days ago'; + const actual = dateTimeFromNow(daysAgo.valueOf(), 0, 0, false); + expect(actual).toBe(expected); + }); + + it('should format as from now with days (in UTC)', () => { + const daysAgo = moment.utc().add(-7, 'd'); + const expected = '7 days ago'; + const actual = dateTimeFromNow(daysAgo.valueOf(), 0, 0, true); + expect(actual).toBe(expected); + }); + + it('should format as from now with minutes', () => { + const daysAgo = moment().add(-2, 'm'); + const expected = '2 minutes ago'; + const actual = dateTimeFromNow(daysAgo.valueOf(), 0, 0, false); + expect(actual).toBe(expected); + }); + + it('should format as from now with minutes (in UTC)', () => { + const daysAgo = moment.utc().add(-2, 'm'); + const expected = '2 minutes ago'; + const actual = dateTimeFromNow(daysAgo.valueOf(), 0, 0, true); + expect(actual).toBe(expected); + }); +}); + +describe('duration', () => { + it('0 milliseconds', () => { + const str = toDurationInMilliseconds(0, 0); + expect(str).toBe('0 milliseconds'); + }); + it('1 millisecond', () => { + const str = toDurationInMilliseconds(1, 0); + expect(str).toBe('1 millisecond'); + }); + it('-1 millisecond', () => { + const str = toDurationInMilliseconds(-1, 0); + expect(str).toBe('1 millisecond ago'); + }); + it('seconds', () => { + const str = toDurationInSeconds(1, 0); + expect(str).toBe('1 second'); + }); + it('minutes', () => { + const str = toDuration(1, 0, Interval.Minute); + expect(str).toBe('1 minute'); + }); + it('hours', () => { + const str = toDuration(1, 0, Interval.Hour); + expect(str).toBe('1 hour'); + }); + it('days', () => { + const str = toDuration(1, 0, Interval.Day); + expect(str).toBe('1 day'); + }); + it('weeks', () => { + const str = toDuration(1, 0, Interval.Week); + expect(str).toBe('1 week'); + }); + it('months', () => { + const str = toDuration(1, 0, Interval.Month); + expect(str).toBe('1 month'); + }); + it('years', () => { + const str = toDuration(1, 0, Interval.Year); + expect(str).toBe('1 year'); + }); + it('decimal days', () => { + const str = toDuration(1.5, 2, Interval.Day); + expect(str).toBe('1 day, 12 hours, 0 minutes'); + }); + it('decimal months', () => { + const str = toDuration(1.5, 3, Interval.Month); + expect(str).toBe('1 month, 2 weeks, 1 day, 0 hours'); + }); + it('no decimals', () => { + const str = toDuration(38898367008, 0, Interval.Millisecond); + expect(str).toBe('1 year'); + }); + it('1 decimal', () => { + const str = toDuration(38898367008, 1, Interval.Millisecond); + expect(str).toBe('1 year, 2 months'); + }); + it('too many decimals', () => { + const str = toDuration(38898367008, 20, Interval.Millisecond); + expect(str).toBe('1 year, 2 months, 3 weeks, 4 days, 5 hours, 6 minutes, 7 seconds, 8 milliseconds'); + }); + it('floating point error', () => { + const str = toDuration(36993906007, 8, Interval.Millisecond); + expect(str).toBe('1 year, 2 months, 0 weeks, 3 days, 4 hours, 5 minutes, 6 seconds, 7 milliseconds'); + }); +}); + +describe('clock', () => { + it('size less than 1 second', () => { + const str = toClock(999, 0); + expect(str).toBe('999ms'); + }); + describe('size less than 1 minute', () => { + it('default', () => { + const str = toClock(59999); + expect(str).toBe('59s:999ms'); + }); + it('decimals equals 0', () => { + const str = toClock(59999, 0); + expect(str).toBe('59s'); + }); + }); + describe('size less than 1 hour', () => { + it('default', () => { + const str = toClock(3599999); + expect(str).toBe('59m:59s:999ms'); + }); + it('decimals equals 0', () => { + const str = toClock(3599999, 0); + expect(str).toBe('59m'); + }); + it('decimals equals 1', () => { + const str = toClock(3599999, 1); + expect(str).toBe('59m:59s'); + }); + }); + describe('size greater than or equal 1 hour', () => { + it('default', () => { + const str = toClock(7199999); + expect(str).toBe('01h:59m:59s:999ms'); + }); + it('decimals equals 0', () => { + const str = toClock(7199999, 0); + expect(str).toBe('01h'); + }); + it('decimals equals 1', () => { + const str = toClock(7199999, 1); + expect(str).toBe('01h:59m'); + }); + it('decimals equals 2', () => { + const str = toClock(7199999, 2); + expect(str).toBe('01h:59m:59s'); + }); + }); + describe('size greater than or equal 1 day', () => { + it('default', () => { + const str = toClock(89999999); + expect(str).toBe('24h:59m:59s:999ms'); + }); + it('decimals equals 0', () => { + const str = toClock(89999999, 0); + expect(str).toBe('24h'); + }); + it('decimals equals 1', () => { + const str = toClock(89999999, 1); + expect(str).toBe('24h:59m'); + }); + it('decimals equals 2', () => { + const str = toClock(89999999, 2); + expect(str).toBe('24h:59m:59s'); + }); + }); +}); diff --git a/packages/grafana-ui/src/utils/ValueFormats/dateTimeFormatters.ts b/packages/grafana-ui/src/utils/ValueFormats/dateTimeFormatters.ts new file mode 100644 index 00000000000..1e07857eb66 --- /dev/null +++ b/packages/grafana-ui/src/utils/ValueFormats/dateTimeFormatters.ts @@ -0,0 +1,312 @@ +import { toFixed, toFixedScaled } from './valueFormats'; +import moment from 'moment'; + +interface IntervalsInSeconds { + [interval: string]: number; +} + +export enum Interval { + Year = 'year', + Month = 'month', + Week = 'week', + Day = 'day', + Hour = 'hour', + Minute = 'minute', + Second = 'second', + Millisecond = 'millisecond', +} + +const INTERVALS_IN_SECONDS: IntervalsInSeconds = { + [Interval.Year]: 31536000, + [Interval.Month]: 2592000, + [Interval.Week]: 604800, + [Interval.Day]: 86400, + [Interval.Hour]: 3600, + [Interval.Minute]: 60, + [Interval.Second]: 1, + [Interval.Millisecond]: 0.001, +}; + +export function toNanoSeconds(size: number, decimals: number, scaledDecimals: number) { + if (size === null) { + return ''; + } + + if (Math.abs(size) < 1000) { + return toFixed(size, decimals) + ' ns'; + } else if (Math.abs(size) < 1000000) { + return toFixedScaled(size / 1000, decimals, scaledDecimals, 3, ' µs'); + } else if (Math.abs(size) < 1000000000) { + return toFixedScaled(size / 1000000, decimals, scaledDecimals, 6, ' ms'); + } else if (Math.abs(size) < 60000000000) { + return toFixedScaled(size / 1000000000, decimals, scaledDecimals, 9, ' s'); + } else { + return toFixedScaled(size / 60000000000, decimals, scaledDecimals, 12, ' min'); + } +} + +export function toMicroSeconds(size: number, decimals: number, scaledDecimals: number) { + if (size === null) { + return ''; + } + + if (Math.abs(size) < 1000) { + return toFixed(size, decimals) + ' µs'; + } else if (Math.abs(size) < 1000000) { + return toFixedScaled(size / 1000, decimals, scaledDecimals, 3, ' ms'); + } else { + return toFixedScaled(size / 1000000, decimals, scaledDecimals, 6, ' s'); + } +} + +export function toMilliSeconds(size: number, decimals: number, scaledDecimals: number) { + if (size === null) { + return ''; + } + + if (Math.abs(size) < 1000) { + return toFixed(size, decimals) + ' ms'; + } else if (Math.abs(size) < 60000) { + // Less than 1 min + return toFixedScaled(size / 1000, decimals, scaledDecimals, 3, ' s'); + } else if (Math.abs(size) < 3600000) { + // Less than 1 hour, divide in minutes + return toFixedScaled(size / 60000, decimals, scaledDecimals, 5, ' min'); + } else if (Math.abs(size) < 86400000) { + // Less than one day, divide in hours + return toFixedScaled(size / 3600000, decimals, scaledDecimals, 7, ' hour'); + } else if (Math.abs(size) < 31536000000) { + // Less than one year, divide in days + return toFixedScaled(size / 86400000, decimals, scaledDecimals, 8, ' day'); + } + + return toFixedScaled(size / 31536000000, decimals, scaledDecimals, 10, ' year'); +} + +export function toSeconds(size: number, decimals: number, scaledDecimals: number) { + if (size === null) { + return ''; + } + + // Less than 1 µs, divide in ns + if (Math.abs(size) < 0.000001) { + return toFixedScaled(size * 1e9, decimals, scaledDecimals - decimals, -9, ' ns'); + } + // Less than 1 ms, divide in µs + if (Math.abs(size) < 0.001) { + return toFixedScaled(size * 1e6, decimals, scaledDecimals - decimals, -6, ' µs'); + } + // Less than 1 second, divide in ms + if (Math.abs(size) < 1) { + return toFixedScaled(size * 1e3, decimals, scaledDecimals - decimals, -3, ' ms'); + } + + if (Math.abs(size) < 60) { + return toFixed(size, decimals) + ' s'; + } else if (Math.abs(size) < 3600) { + // Less than 1 hour, divide in minutes + return toFixedScaled(size / 60, decimals, scaledDecimals, 1, ' min'); + } else if (Math.abs(size) < 86400) { + // Less than one day, divide in hours + return toFixedScaled(size / 3600, decimals, scaledDecimals, 4, ' hour'); + } else if (Math.abs(size) < 604800) { + // Less than one week, divide in days + return toFixedScaled(size / 86400, decimals, scaledDecimals, 5, ' day'); + } else if (Math.abs(size) < 31536000) { + // Less than one year, divide in week + return toFixedScaled(size / 604800, decimals, scaledDecimals, 6, ' week'); + } + + return toFixedScaled(size / 3.15569e7, decimals, scaledDecimals, 7, ' year'); +} + +export function toMinutes(size: number, decimals: number, scaledDecimals: number) { + if (size === null) { + return ''; + } + + if (Math.abs(size) < 60) { + return toFixed(size, decimals) + ' min'; + } else if (Math.abs(size) < 1440) { + return toFixedScaled(size / 60, decimals, scaledDecimals, 2, ' hour'); + } else if (Math.abs(size) < 10080) { + return toFixedScaled(size / 1440, decimals, scaledDecimals, 3, ' day'); + } else if (Math.abs(size) < 604800) { + return toFixedScaled(size / 10080, decimals, scaledDecimals, 4, ' week'); + } else { + return toFixedScaled(size / 5.25948e5, decimals, scaledDecimals, 5, ' year'); + } +} + +export function toHours(size: number, decimals: number, scaledDecimals: number) { + if (size === null) { + return ''; + } + + if (Math.abs(size) < 24) { + return toFixed(size, decimals) + ' hour'; + } else if (Math.abs(size) < 168) { + return toFixedScaled(size / 24, decimals, scaledDecimals, 2, ' day'); + } else if (Math.abs(size) < 8760) { + return toFixedScaled(size / 168, decimals, scaledDecimals, 3, ' week'); + } else { + return toFixedScaled(size / 8760, decimals, scaledDecimals, 4, ' year'); + } +} + +export function toDays(size: number, decimals: number, scaledDecimals: number) { + if (size === null) { + return ''; + } + + if (Math.abs(size) < 7) { + return toFixed(size, decimals) + ' day'; + } else if (Math.abs(size) < 365) { + return toFixedScaled(size / 7, decimals, scaledDecimals, 2, ' week'); + } else { + return toFixedScaled(size / 365, decimals, scaledDecimals, 3, ' year'); + } +} + +export function toDuration(size: number, decimals: number, timeScale: Interval): string { + if (size === null) { + return ''; + } + if (size === 0) { + return '0 ' + timeScale + 's'; + } + if (size < 0) { + return toDuration(-size, decimals, timeScale) + ' ago'; + } + + const units = [ + { long: Interval.Year }, + { long: Interval.Month }, + { long: Interval.Week }, + { long: Interval.Day }, + { long: Interval.Hour }, + { long: Interval.Minute }, + { long: Interval.Second }, + { long: Interval.Millisecond }, + ]; + // convert $size to milliseconds + // intervals_in_seconds uses seconds (duh), convert them to milliseconds here to minimize floating point errors + size *= INTERVALS_IN_SECONDS[timeScale] * 1000; + + const strings = []; + // after first value >= 1 print only $decimals more + let decrementDecimals = false; + for (let i = 0; i < units.length && decimals >= 0; i++) { + const interval = INTERVALS_IN_SECONDS[units[i].long] * 1000; + const value = size / interval; + if (value >= 1 || decrementDecimals) { + decrementDecimals = true; + const floor = Math.floor(value); + const unit = units[i].long + (floor !== 1 ? 's' : ''); + strings.push(floor + ' ' + unit); + size = size % interval; + decimals--; + } + } + + return strings.join(', '); +} + +export function toClock(size: number, decimals?: number) { + if (size === null) { + return ''; + } + + // < 1 second + if (size < 1000) { + return moment.utc(size).format('SSS\\m\\s'); + } + + // < 1 minute + if (size < 60000) { + let format = 'ss\\s:SSS\\m\\s'; + if (decimals === 0) { + format = 'ss\\s'; + } + return moment.utc(size).format(format); + } + + // < 1 hour + if (size < 3600000) { + let format = 'mm\\m:ss\\s:SSS\\m\\s'; + if (decimals === 0) { + format = 'mm\\m'; + } else if (decimals === 1) { + format = 'mm\\m:ss\\s'; + } + return moment.utc(size).format(format); + } + + let format = 'mm\\m:ss\\s:SSS\\m\\s'; + + const hours = `${('0' + Math.floor(moment.duration(size, 'milliseconds').asHours())).slice(-2)}h`; + + if (decimals === 0) { + format = ''; + } else if (decimals === 1) { + format = 'mm\\m'; + } else if (decimals === 2) { + format = 'mm\\m:ss\\s'; + } + + return format ? `${hours}:${moment.utc(size).format(format)}` : hours; +} + +export function toDurationInMilliseconds(size: number, decimals: number) { + return toDuration(size, decimals, Interval.Millisecond); +} + +export function toDurationInSeconds(size: number, decimals: number) { + return toDuration(size, decimals, Interval.Second); +} + +export function toDurationInHoursMinutesSeconds(size: number) { + const strings = []; + const numHours = Math.floor(size / 3600); + const numMinutes = Math.floor((size % 3600) / 60); + const numSeconds = Math.floor((size % 3600) % 60); + numHours > 9 ? strings.push('' + numHours) : strings.push('0' + numHours); + numMinutes > 9 ? strings.push('' + numMinutes) : strings.push('0' + numMinutes); + numSeconds > 9 ? strings.push('' + numSeconds) : strings.push('0' + numSeconds); + return strings.join(':'); +} + +export function toTimeTicks(size: number, decimals: number, scaledDecimals: number) { + return toSeconds(size, decimals, scaledDecimals); +} + +export function toClockMilliseconds(size: number, decimals: number) { + return toClock(size, decimals); +} + +export function toClockSeconds(size: number, decimals: number) { + return toClock(size * 1000, decimals); +} + +export function dateTimeAsIso(value: number, decimals: number, scaledDecimals: number, isUtc: boolean) { + const time = isUtc ? moment.utc(value) : moment(value); + + if (moment().isSame(value, 'day')) { + return time.format('HH:mm:ss'); + } + return time.format('YYYY-MM-DD HH:mm:ss'); +} + +export function dateTimeAsUS(value: number, decimals: number, scaledDecimals: number, isUtc: boolean) { + const time = isUtc ? moment.utc(value) : moment(value); + + if (moment().isSame(value, 'day')) { + return time.format('h:mm:ss a'); + } + return time.format('MM/DD/YYYY h:mm:ss a'); +} + +export function dateTimeFromNow(value: number, decimals: number, scaledDecimals: number, isUtc: boolean) { + const time = isUtc ? moment.utc(value) : moment(value); + return time.fromNow(); +} diff --git a/packages/grafana-ui/src/utils/ValueFormats/symbolFormatters.test.ts b/packages/grafana-ui/src/utils/ValueFormats/symbolFormatters.test.ts new file mode 100644 index 00000000000..6b91a49c0dc --- /dev/null +++ b/packages/grafana-ui/src/utils/ValueFormats/symbolFormatters.test.ts @@ -0,0 +1,11 @@ +import { currency } from './symbolFormatters'; + +describe('Currency', () => { + it('should format as usd', () => { + expect(currency('$')(1532.82, 1, -1)).toEqual('$1.53K'); + }); + + it('should format as usd', () => { + expect(currency('kr')(1532.82, 1, -1)).toEqual('1.53K kr'); + }); +}); diff --git a/packages/grafana-ui/src/utils/ValueFormats/symbolFormatters.ts b/packages/grafana-ui/src/utils/ValueFormats/symbolFormatters.ts new file mode 100644 index 00000000000..66808143daa --- /dev/null +++ b/packages/grafana-ui/src/utils/ValueFormats/symbolFormatters.ts @@ -0,0 +1,30 @@ +import { scaledUnits } from './valueFormats'; + +export function currency(symbol: string) { + const units = ['', 'K', 'M', 'B', 'T']; + const scaler = scaledUnits(1000, units); + return (size: number, decimals: number, scaledDecimals: number) => { + if (size === null) { + return ''; + } + const scaled = scaler(size, decimals, scaledDecimals); + return symbol + scaled; + }; +} + +export function binarySIPrefix(unit: string, offset = 0) { + const prefixes = ['', 'Ki', 'Mi', 'Gi', 'Ti', 'Pi', 'Ei', 'Zi', 'Yi'].slice(offset); + const units = prefixes.map(p => { + return ' ' + p + unit; + }); + return scaledUnits(1024, units); +} + +export function decimalSIPrefix(unit: string, offset = 0) { + let prefixes = ['n', 'µ', 'm', '', 'k', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y']; + prefixes = prefixes.slice(3 + (offset || 0)); + const units = prefixes.map(p => { + return ' ' + p + unit; + }); + return scaledUnits(1000, units); +} diff --git a/packages/grafana-ui/src/utils/ValueFormats/valueFormats.ts b/packages/grafana-ui/src/utils/ValueFormats/valueFormats.ts new file mode 100644 index 00000000000..152f242b1a8 --- /dev/null +++ b/packages/grafana-ui/src/utils/ValueFormats/valueFormats.ts @@ -0,0 +1,154 @@ +import { getCategories } from './categories'; + +type ValueFormatter = (value: number, decimals?: number, scaledDecimals?: number, isUtc?: boolean) => string; + +interface ValueFormat { + name: string; + id: string; + fn: ValueFormatter; +} + +export interface ValueFormatCategory { + name: string; + formats: ValueFormat[]; +} + +interface ValueFormatterIndex { + [id: string]: ValueFormatter; +} + +// Globals & formats cache +let categories: ValueFormatCategory[] = []; +const index: ValueFormatterIndex = {}; +let hasBuildIndex = false; + +export function toFixed(value: number, decimals?: number): string { + if (value === null) { + return ''; + } + + const factor = decimals ? Math.pow(10, Math.max(0, decimals)) : 1; + const formatted = String(Math.round(value * factor) / factor); + + // if exponent return directly + if (formatted.indexOf('e') !== -1 || value === 0) { + return formatted; + } + + // If tickDecimals was specified, ensure that we have exactly that + // much precision; otherwise default to the value's own precision. + if (decimals != null) { + const decimalPos = formatted.indexOf('.'); + const precision = decimalPos === -1 ? 0 : formatted.length - decimalPos - 1; + if (precision < decimals) { + return (precision ? formatted : formatted + '.') + String(factor).substr(1, decimals - precision); + } + } + + return formatted; +} + +export function toFixedScaled( + value: number, + decimals: number, + scaledDecimals: number, + additionalDecimals: number, + ext: string +) { + if (scaledDecimals === null) { + return toFixed(value, decimals) + ext; + } else { + return toFixed(value, scaledDecimals + additionalDecimals) + ext; + } +} + +export function toFixedUnit(unit: string) { + return (size: number, decimals: number) => { + if (size === null) { + return ''; + } + return toFixed(size, decimals) + ' ' + unit; + }; +} + +// Formatter which scales the unit string geometrically according to the given +// numeric factor. Repeatedly scales the value down by the factor until it is +// less than the factor in magnitude, or the end of the array is reached. +export function scaledUnits(factor: number, extArray: string[]) { + return (size: number, decimals: number, scaledDecimals: number) => { + if (size === null) { + return ''; + } + + let steps = 0; + const limit = extArray.length; + + while (Math.abs(size) >= factor) { + steps++; + size /= factor; + + if (steps >= limit) { + return 'NA'; + } + } + + if (steps > 0 && scaledDecimals !== null) { + decimals = scaledDecimals + 3 * steps; + } + + return toFixed(size, decimals) + extArray[steps]; + }; +} + +export function locale(value: number, decimals: number) { + if (value == null) { + return ''; + } + return value.toLocaleString(undefined, { maximumFractionDigits: decimals }); +} + +function buildFormats() { + categories = getCategories(); + + for (const cat of categories) { + for (const format of cat.formats) { + index[format.id] = format.fn; + } + } + + hasBuildIndex = true; +} + +export function getValueFormat(id: string): ValueFormatter { + if (!hasBuildIndex) { + buildFormats(); + } + + return index[id]; +} + +export function getValueFormatterIndex(): ValueFormatterIndex { + if (!hasBuildIndex) { + buildFormats(); + } + + return index; +} + +export function getUnitFormats() { + if (!hasBuildIndex) { + buildFormats(); + } + + return categories.map(cat => { + return { + text: cat.name, + submenu: cat.formats.map(format => { + return { + text: format.name, + id: format.id, + }; + }), + }; + }); +} diff --git a/packages/grafana-ui/src/utils/index.ts b/packages/grafana-ui/src/utils/index.ts index cd46a5b6424..b1804c8605e 100644 --- a/packages/grafana-ui/src/utils/index.ts +++ b/packages/grafana-ui/src/utils/index.ts @@ -1,2 +1,2 @@ export * from './processTimeSeries'; -export * from './valueFormats'; +export * from './ValueFormats/valueFormats'; diff --git a/packages/grafana-ui/src/utils/valueFormats.ts b/packages/grafana-ui/src/utils/valueFormats.ts deleted file mode 100644 index 5e86a583bb4..00000000000 --- a/packages/grafana-ui/src/utils/valueFormats.ts +++ /dev/null @@ -1,824 +0,0 @@ -import moment from 'moment'; - -type ValueFormatter = (value: number, decimals?: number, scaledDecimals?: number, isUtc?: boolean) => string; - -interface ValueFormat { - name: string; - id: string; - fn: ValueFormatter; -} - -interface ValueFormatCategory { - name: string; - formats: ValueFormat[]; -} - -interface ValueFormatterIndex { - [id: string]: ValueFormatter; -} - -interface IntervalsInSeconds { - [interval: string]: number; -} - -enum Interval { - Year = 'year', - Month = 'month', - Week = 'week', - Day = 'day', - Hour = 'hour', - Minute = 'minute', - Second = 'second', - Millisecond = 'millisecond', -} - -const INTERVALS_IN_SECONDS: IntervalsInSeconds = { - [Interval.Year]: 31536000, - [Interval.Month]: 2592000, - [Interval.Week]: 604800, - [Interval.Day]: 86400, - [Interval.Hour]: 3600, - [Interval.Month]: 60, - [Interval.Second]: 1, - [Interval.Millisecond]: 0.001, -}; - -// Globals & formats cache -let categories: ValueFormatCategory[] = []; -const index: ValueFormatterIndex = {}; -let hasBuildIndex = false; - -function toFixed(value: number, decimals?: number): string { - if (value === null) { - return ''; - } - - const factor = decimals ? Math.pow(10, Math.max(0, decimals)) : 1; - const formatted = String(Math.round(value * factor) / factor); - - // if exponent return directly - if (formatted.indexOf('e') !== -1 || value === 0) { - return formatted; - } - - // If tickDecimals was specified, ensure that we have exactly that - // much precision; otherwise default to the value's own precision. - if (decimals != null) { - const decimalPos = formatted.indexOf('.'); - const precision = decimalPos === -1 ? 0 : formatted.length - decimalPos - 1; - if (precision < decimals) { - return (precision ? formatted : formatted + '.') + String(factor).substr(1, decimals - precision); - } - } - - return formatted; -} - -function toFixedScaled( - value: number, - decimals: number, - scaledDecimals: number, - additionalDecimals: number, - ext: string -) { - if (scaledDecimals === null) { - return toFixed(value, decimals) + ext; - } else { - return toFixed(value, scaledDecimals + additionalDecimals) + ext; - } -} - -function toFixedUnit(unit: string) { - return (size: number, decimals: number) => { - if (size === null) { - return ''; - } - return toFixed(size, decimals) + ' ' + unit; - }; -} - -// Formatter which scales the unit string geometrically according to the given -// numeric factor. Repeatedly scales the value down by the factor until it is -// less than the factor in magnitude, or the end of the array is reached. -function scaledUnits(factor: number, extArray: string[]) { - return (size: number, decimals: number, scaledDecimals: number) => { - if (size === null) { - return ''; - } - - let steps = 0; - const limit = extArray.length; - - while (Math.abs(size) >= factor) { - steps++; - size /= factor; - - if (steps >= limit) { - return 'NA'; - } - } - - if (steps > 0 && scaledDecimals !== null) { - decimals = scaledDecimals + 3 * steps; - } - - return toFixed(size, decimals) + extArray[steps]; - }; -} - -function toPercent(size: number, decimals: number) { - if (size === null) { - return ''; - } - return toFixed(size, decimals) + '%'; -} - -function toPercentUnit(size: number, decimals: number) { - if (size === null) { - return ''; - } - return toFixed(100 * size, decimals) + '%'; -} - -function toHex0x(value: number, decimals: number) { - if (value == null) { - return ''; - } - const hexString = hex(value, decimals); - if (hexString.substring(0, 1) === '-') { - return '-0x' + hexString.substring(1); - } - return '0x' + hexString; -} - -function hex(value: number, decimals: number) { - if (value == null) { - return ''; - } - return parseFloat(toFixed(value, decimals)) - .toString(16) - .toUpperCase(); -} - -function sci(value: number, decimals: number) { - if (value == null) { - return ''; - } - return value.toExponential(decimals); -} - -function locale(value: number, decimals: number) { - if (value == null) { - return ''; - } - return value.toLocaleString(undefined, { maximumFractionDigits: decimals }); -} - -function currency(symbol: string) { - const units = ['', 'K', 'M', 'B', 'T']; - const scaler = scaledUnits(1000, units); - return (size: number, decimals: number, scaledDecimals: number) => { - if (size === null) { - return ''; - } - const scaled = scaler(size, decimals, scaledDecimals); - return symbol + scaled; - }; -} - -function toNanoSeconds(size: number, decimals: number, scaledDecimals: number) { - if (size === null) { - return ''; - } - - if (Math.abs(size) < 1000) { - return toFixed(size, decimals) + ' ns'; - } else if (Math.abs(size) < 1000000) { - return toFixedScaled(size / 1000, decimals, scaledDecimals, 3, ' µs'); - } else if (Math.abs(size) < 1000000000) { - return toFixedScaled(size / 1000000, decimals, scaledDecimals, 6, ' ms'); - } else if (Math.abs(size) < 60000000000) { - return toFixedScaled(size / 1000000000, decimals, scaledDecimals, 9, ' s'); - } else { - return toFixedScaled(size / 60000000000, decimals, scaledDecimals, 12, ' min'); - } -} - -function toMicroSeconds(size: number, decimals: number, scaledDecimals: number) { - if (size === null) { - return ''; - } - - if (Math.abs(size) < 1000) { - return toFixed(size, decimals) + ' µs'; - } else if (Math.abs(size) < 1000000) { - return toFixedScaled(size / 1000, decimals, scaledDecimals, 3, ' ms'); - } else { - return toFixedScaled(size / 1000000, decimals, scaledDecimals, 6, ' s'); - } -} - -function toMilliSeconds(size: number, decimals: number, scaledDecimals: number) { - if (size === null) { - return ''; - } - - if (Math.abs(size) < 1000) { - return toFixed(size, decimals) + ' ms'; - } else if (Math.abs(size) < 60000) { - // Less than 1 min - return toFixedScaled(size / 1000, decimals, scaledDecimals, 3, ' s'); - } else if (Math.abs(size) < 3600000) { - // Less than 1 hour, divide in minutes - return toFixedScaled(size / 60000, decimals, scaledDecimals, 5, ' min'); - } else if (Math.abs(size) < 86400000) { - // Less than one day, divide in hours - return toFixedScaled(size / 3600000, decimals, scaledDecimals, 7, ' hour'); - } else if (Math.abs(size) < 31536000000) { - // Less than one year, divide in days - return toFixedScaled(size / 86400000, decimals, scaledDecimals, 8, ' day'); - } - - return toFixedScaled(size / 31536000000, decimals, scaledDecimals, 10, ' year'); -} - -function toSeconds(size: number, decimals: number, scaledDecimals: number) { - if (size === null) { - return ''; - } - - // Less than 1 µs, divide in ns - if (Math.abs(size) < 0.000001) { - return toFixedScaled(size * 1e9, decimals, scaledDecimals - decimals, -9, ' ns'); - } - // Less than 1 ms, divide in µs - if (Math.abs(size) < 0.001) { - return toFixedScaled(size * 1e6, decimals, scaledDecimals - decimals, -6, ' µs'); - } - // Less than 1 second, divide in ms - if (Math.abs(size) < 1) { - return toFixedScaled(size * 1e3, decimals, scaledDecimals - decimals, -3, ' ms'); - } - - if (Math.abs(size) < 60) { - return toFixed(size, decimals) + ' s'; - } else if (Math.abs(size) < 3600) { - // Less than 1 hour, divide in minutes - return toFixedScaled(size / 60, decimals, scaledDecimals, 1, ' min'); - } else if (Math.abs(size) < 86400) { - // Less than one day, divide in hours - return toFixedScaled(size / 3600, decimals, scaledDecimals, 4, ' hour'); - } else if (Math.abs(size) < 604800) { - // Less than one week, divide in days - return toFixedScaled(size / 86400, decimals, scaledDecimals, 5, ' day'); - } else if (Math.abs(size) < 31536000) { - // Less than one year, divide in week - return toFixedScaled(size / 604800, decimals, scaledDecimals, 6, ' week'); - } - - return toFixedScaled(size / 3.15569e7, decimals, scaledDecimals, 7, ' year'); -} - -function toMinutes(size: number, decimals: number, scaledDecimals: number) { - if (size === null) { - return ''; - } - - if (Math.abs(size) < 60) { - return toFixed(size, decimals) + ' min'; - } else if (Math.abs(size) < 1440) { - return toFixedScaled(size / 60, decimals, scaledDecimals, 2, ' hour'); - } else if (Math.abs(size) < 10080) { - return toFixedScaled(size / 1440, decimals, scaledDecimals, 3, ' day'); - } else if (Math.abs(size) < 604800) { - return toFixedScaled(size / 10080, decimals, scaledDecimals, 4, ' week'); - } else { - return toFixedScaled(size / 5.25948e5, decimals, scaledDecimals, 5, ' year'); - } -} - -function toHours(size: number, decimals: number, scaledDecimals: number) { - if (size === null) { - return ''; - } - - if (Math.abs(size) < 24) { - return toFixed(size, decimals) + ' hour'; - } else if (Math.abs(size) < 168) { - return toFixedScaled(size / 24, decimals, scaledDecimals, 2, ' day'); - } else if (Math.abs(size) < 8760) { - return toFixedScaled(size / 168, decimals, scaledDecimals, 3, ' week'); - } else { - return toFixedScaled(size / 8760, decimals, scaledDecimals, 4, ' year'); - } -} - -function toDays(size: number, decimals: number, scaledDecimals: number) { - if (size === null) { - return ''; - } - - if (Math.abs(size) < 7) { - return toFixed(size, decimals) + ' day'; - } else if (Math.abs(size) < 365) { - return toFixedScaled(size / 7, decimals, scaledDecimals, 2, ' week'); - } else { - return toFixedScaled(size / 365, decimals, scaledDecimals, 3, ' year'); - } -} - -function toDuration(size: number, decimals: number, timeScale: Interval): string { - if (size === null) { - return ''; - } - if (size === 0) { - return '0 ' + timeScale + 's'; - } - if (size < 0) { - return toDuration(-size, decimals, timeScale) + ' ago'; - } - - const units = [ - { long: Interval.Year }, - { long: Interval.Month }, - { long: Interval.Week }, - { long: Interval.Day }, - { long: Interval.Hour }, - { long: Interval.Minute }, - { long: Interval.Second }, - { long: Interval.Millisecond }, - ]; - // convert $size to milliseconds - // intervals_in_seconds uses seconds (duh), convert them to milliseconds here to minimize floating point errors - size *= INTERVALS_IN_SECONDS[timeScale] * 1000; - - const strings = []; - // after first value >= 1 print only $decimals more - let decrementDecimals = false; - for (let i = 0; i < units.length && decimals >= 0; i++) { - const interval = INTERVALS_IN_SECONDS[units[i].long] * 1000; - const value = size / interval; - if (value >= 1 || decrementDecimals) { - decrementDecimals = true; - const floor = Math.floor(value); - const unit = units[i].long + (floor !== 1 ? 's' : ''); - strings.push(floor + ' ' + unit); - size = size % interval; - decimals--; - } - } - - return strings.join(', '); -} - -function toClock(size: number, decimals: number) { - if (size === null) { - return ''; - } - - // < 1 second - if (size < 1000) { - return moment.utc(size).format('SSS\\m\\s'); - } - - // < 1 minute - if (size < 60000) { - let format = 'ss\\s:SSS\\m\\s'; - if (decimals === 0) { - format = 'ss\\s'; - } - return moment.utc(size).format(format); - } - - // < 1 hour - if (size < 3600000) { - let format = 'mm\\m:ss\\s:SSS\\m\\s'; - if (decimals === 0) { - format = 'mm\\m'; - } else if (decimals === 1) { - format = 'mm\\m:ss\\s'; - } - return moment.utc(size).format(format); - } - - let format = 'mm\\m:ss\\s:SSS\\m\\s'; - - const hours = `${('0' + Math.floor(moment.duration(size, 'milliseconds').asHours())).slice(-2)}h`; - - if (decimals === 0) { - format = ''; - } else if (decimals === 1) { - format = 'mm\\m'; - } else if (decimals === 2) { - format = 'mm\\m:ss\\s'; - } - - return format ? `${hours}:${moment.utc(size).format(format)}` : hours; -} - -function toDurationInMilliseconds(size: number, decimals: number) { - return toDuration(size, decimals, Interval.Millisecond); -} - -function toDurationInSeconds(size: number, decimals: number) { - return toDuration(size, decimals, Interval.Second); -} - -function toDurationInHoursMinutesSeconds(size: number) { - const strings = []; - const numHours = Math.floor(size / 3600); - const numMinutes = Math.floor((size % 3600) / 60); - const numSeconds = Math.floor((size % 3600) % 60); - numHours > 9 ? strings.push('' + numHours) : strings.push('0' + numHours); - numMinutes > 9 ? strings.push('' + numMinutes) : strings.push('0' + numMinutes); - numSeconds > 9 ? strings.push('' + numSeconds) : strings.push('0' + numSeconds); - return strings.join(':'); -} - -function toTimeTicks(size: number, decimals: number, scaledDecimals: number) { - return toSeconds(size, decimals, scaledDecimals); -} - -function toClockMilliseconds(size: number, decimals: number) { - return toClock(size, decimals); -} - -function toClockSeconds(size: number, decimals: number) { - return toClock(size * 1000, decimals); -} - -function dateTimeAsIso(value: number, decimals: number, scaledDecimals: number, isUtc: boolean) { - const time = isUtc ? moment.utc(value) : moment(value); - - if (moment().isSame(value, 'day')) { - return time.format('HH:mm:ss'); - } - return time.format('YYYY-MM-DD HH:mm:ss'); -} - -function dateTimeAsUS(value: number, decimals: number, scaledDecimals: number, isUtc: boolean) { - const time = isUtc ? moment.utc(value) : moment(value); - - if (moment().isSame(value, 'day')) { - return time.format('h:mm:ss a'); - } - return time.format('MM/DD/YYYY h:mm:ss a'); -} - -function dateTimeFromNow(value: number, decimals: number, scaledDecimals: number, isUtc: boolean) { - const time = isUtc ? moment.utc(value) : moment(value); - return time.fromNow(); -} - -function binarySIPrefix(unit: string, offset = 0) { - const prefixes = ['', 'Ki', 'Mi', 'Gi', 'Ti', 'Pi', 'Ei', 'Zi', 'Yi'].slice(offset); - const units = prefixes.map(p => { - return ' ' + p + unit; - }); - return scaledUnits(1024, units); -} - -function decimalSIPrefix(unit: string, offset = 0) { - let prefixes = ['n', 'µ', 'm', '', 'k', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y']; - prefixes = prefixes.slice(3 + (offset || 0)); - const units = prefixes.map(p => { - return ' ' + p + unit; - }); - return scaledUnits(1000, units); -} - -function buildFormats() { - categories = [ - { - name: 'none', - formats: [ - { name: 'none', id: 'none', fn: toFixed }, - { - name: 'short', - id: 'short', - fn: scaledUnits(1000, ['', ' K', ' Mil', ' Bil', ' Tri', ' Quadr', ' Quint', ' Sext', ' Sept']), - }, - { name: 'percent (0-100)', id: 'percent', fn: toPercent }, - { name: 'percent (0.0-1.0)', id: 'percentunit', fn: toPercentUnit }, - { name: 'Humidity (%H)', id: 'humidity', fn: toFixedUnit('%H') }, - { name: 'decibel', id: 'dB', fn: toFixedUnit('dB') }, - { name: 'hexadecimal (0x)', id: 'hex0x', fn: toHex0x }, - { name: 'hexadecimal', id: 'hex', fn: hex }, - { name: 'scientific notation', id: 'sci', fn: sci }, - { name: 'locale format', id: 'locale', fn: locale }, - ], - }, - { - name: 'acceleration', - formats: [ - { name: 'Meters/sec²', id: 'accMS2', fn: toFixedUnit('m/sec²') }, - { name: 'Feet/sec²', id: 'accFS2', fn: toFixedUnit('f/sec²') }, - { name: 'G unit', id: 'accG', fn: toFixedUnit('g') }, - ], - }, - { - name: 'angle', - formats: [ - { name: 'Degrees (°)', id: 'degree', fn: toFixedUnit('°') }, - { name: 'Radians', id: 'radian', fn: toFixedUnit('rad') }, - { name: 'Gradian', id: 'grad', fn: toFixedUnit('grad') }, - ], - }, - { - name: 'area', - formats: [ - { name: 'Square Meters (m²)', id: 'areaM2', fn: toFixedUnit('m²') }, - { name: 'Square Feet (ft²)', id: 'areaF2', fn: toFixedUnit('ft²') }, - { name: 'Square Miles (mi²)', id: 'areaMI2', fn: toFixedUnit('mi²') }, - ], - }, - { - name: 'computation throughput', - formats: [ - { name: 'FLOP/s', id: 'flops', fn: decimalSIPrefix('FLOP/s') }, - { name: 'MFLOP/s', id: 'mflops', fn: decimalSIPrefix('FLOP/s', 2) }, - { name: 'GFLOP/s', id: 'gflops', fn: decimalSIPrefix('FLOP/s', 3) }, - { name: 'TFLOP/s', id: 'tflops', fn: decimalSIPrefix('FLOP/s', 4) }, - { name: 'PFLOP/s', id: 'pflops', fn: decimalSIPrefix('FLOP/s', 5) }, - { name: 'EFLOP/s', id: 'eflops', fn: decimalSIPrefix('FLOP/s', 6) }, - ], - }, - { - name: 'concentration', - formats: [ - { name: 'parts-per-million (ppm)', id: 'ppm', fn: toFixedUnit('ppm') }, - { name: 'parts-per-billion (ppb)', id: 'conppb', fn: toFixedUnit('ppb') }, - { name: 'nanogram per cubic meter (ng/m³)', id: 'conngm3', fn: toFixedUnit('ng/m³') }, - { name: 'nanogram per normal cubic meter (ng/Nm³)', id: 'conngNm3', fn: toFixedUnit('ng/Nm³') }, - { name: 'microgram per cubic meter (μg/m³)', id: 'conμgm3', fn: toFixedUnit('μg/m³') }, - { name: 'microgram per normal cubic meter (μg/Nm³)', id: 'conμgNm3', fn: toFixedUnit('μg/Nm³') }, - { name: 'milligram per cubic meter (mg/m³)', id: 'conmgm3', fn: toFixedUnit('mg/m³') }, - { name: 'milligram per normal cubic meter (mg/Nm³)', id: 'conmgNm3', fn: toFixedUnit('mg/Nm³') }, - { name: 'gram per cubic meter (g/m³)', id: 'congm3', fn: toFixedUnit('g/m³') }, - { name: 'gram per normal cubic meter (g/Nm³)', id: 'congNm3', fn: toFixedUnit('g/Nm³') }, - { name: 'milligrams per decilitre (mg/dL)', id: 'conmgdL', fn: toFixedUnit('mg/dL') }, - { name: 'millimoles per litre (mmol/L)', id: 'conmmolL', fn: toFixedUnit('mmol/L') }, - ], - }, - { - name: 'currency', - formats: [ - { name: 'Dollars ($)', id: 'currencyUSD', fn: currency('$') }, - { name: 'Pounds (£)', id: 'currencyGBP', fn: currency('£') }, - { name: 'Euro (€)', id: 'currencyEUR', fn: currency('€') }, - { name: 'Yen (¥)', id: 'currencyJPY', fn: currency('¥') }, - { name: 'Rubles (₽)', id: 'currencyRUB', fn: currency('₽') }, - { name: 'Hryvnias (₴)', id: 'currencyUAH', fn: currency('₴') }, - { name: 'Real (R$)', id: 'currencyBRL', fn: currency('R$') }, - { name: 'Danish Krone (kr)', id: 'currencyDKK', fn: currency('kr') }, - { name: 'Icelandic Króna (kr)', id: 'currencyISK', fn: currency('kr') }, - { name: 'Norwegian Krone (kr)', id: 'currencyNOK', fn: currency('kr') }, - { name: 'Swedish Krona (kr)', id: 'currencySEK', fn: currency('kr') }, - { name: 'Czech koruna (czk)', id: 'currencyCZK', fn: currency('czk') }, - { name: 'Swiss franc (CHF)', id: 'currencyCHF', fn: currency('CHF') }, - { name: 'Polish Złoty (PLN)', id: 'currencyPLN', fn: currency('PLN') }, - { name: 'Bitcoin (฿)', id: 'currencyBTC', fn: currency('฿') }, - ], - }, - { - name: 'data (IEC)', - formats: [ - { name: 'bits', id: 'bits', fn: binarySIPrefix('b') }, - { name: 'bytes', id: 'bytes', fn: binarySIPrefix('B') }, - { name: 'kibibytes', id: 'kbytes', fn: binarySIPrefix('B', 1) }, - { name: 'mebibytes', id: 'mbytes', fn: binarySIPrefix('B', 2) }, - { name: 'gibibytes', id: 'gbytes', fn: binarySIPrefix('B', 3) }, - ], - }, - { - name: 'data (Metric)', - formats: [ - { name: 'bits', id: 'decbits', fn: decimalSIPrefix('d') }, - { name: 'bytes', id: 'decbytes', fn: decimalSIPrefix('B') }, - { name: 'kilobytes', id: 'deckbytes', fn: decimalSIPrefix('B', 1) }, - { name: 'megabytes', id: 'decmbytes', fn: decimalSIPrefix('B', 2) }, - { name: 'gigabytes', id: 'decgbytes', fn: decimalSIPrefix('B', 3) }, - ], - }, - { - name: 'data rate', - formats: [ - { name: 'packets/sec', id: 'pps', fn: decimalSIPrefix('pps') }, - { name: 'bits/sec', id: 'bps', fn: decimalSIPrefix('bps') }, - { name: 'bytes/sec', id: 'Bps', fn: decimalSIPrefix('B/s') }, - { name: 'kilobytes/sec', id: 'KBs', fn: decimalSIPrefix('Bs', 1) }, - { name: 'kilobits/sec', id: 'Kbits', fn: decimalSIPrefix('bps', 1) }, - { name: 'megabytes/sec', id: 'MBs', fn: decimalSIPrefix('Bs', 2) }, - { name: 'megabits/sec', id: 'Mbits', fn: decimalSIPrefix('bps', 2) }, - { name: 'gigabytes/sec', id: 'GBs', fn: decimalSIPrefix('Bs', 3) }, - { name: 'gigabits/sec', id: 'Gbits', fn: decimalSIPrefix('bps', 3) }, - ], - }, - { - name: 'date & time', - formats: [ - { name: 'YYYY-MM-DD HH:mm:ss', id: 'dateTimeAsIso', fn: dateTimeAsIso }, - { name: 'DD/MM/YYYY h:mm:ss a', id: 'dateTimeAsUS', fn: dateTimeAsUS }, - { name: 'From Now', id: 'dateTimeFromNow', fn: dateTimeFromNow }, - ], - }, - { - name: 'energy', - formats: [ - { name: 'Watt (W)', id: 'watt', fn: decimalSIPrefix('W') }, - { name: 'Kilowatt (kW)', id: 'kwatt', fn: decimalSIPrefix('W', 1) }, - { name: 'Milliwatt (mW)', id: 'mwatt', fn: decimalSIPrefix('W', -1) }, - { name: 'Watt per square meter (W/m²)', id: 'Wm2', fn: toFixedUnit('W/m²') }, - { name: 'Volt-ampere (VA)', id: 'voltamp', fn: decimalSIPrefix('VA') }, - { name: 'Kilovolt-ampere (kVA)', id: 'kvoltamp', fn: decimalSIPrefix('VA', 1) }, - { name: 'Volt-ampere reactive (var)', id: 'voltampreact', fn: decimalSIPrefix('var') }, - { name: 'Kilovolt-ampere reactive (kvar)', id: 'kvoltampreact', fn: decimalSIPrefix('var', 1) }, - { name: 'Watt-hour (Wh)', id: 'watth', fn: decimalSIPrefix('Wh') }, - { name: 'Kilowatt-hour (kWh)', id: 'kwatth', fn: decimalSIPrefix('Wh', 1) }, - { name: 'Kilowatt-min (kWm)', id: 'kwattm', fn: decimalSIPrefix('W/Min', 1) }, - { name: 'Joule (J)', id: 'joule', fn: decimalSIPrefix('J') }, - { name: 'Electron volt (eV)', id: 'ev', fn: decimalSIPrefix('eV') }, - { name: 'Ampere (A)', id: 'amp', fn: decimalSIPrefix('A') }, - { name: 'Kiloampere (kA)', id: 'kamp', fn: decimalSIPrefix('A', 1) }, - { name: 'Milliampere (mA)', id: 'mamp', fn: decimalSIPrefix('A', -1) }, - { name: 'Volt (V)', id: 'volt', fn: decimalSIPrefix('V') }, - { name: 'Kilovolt (kV)', id: 'kvolt', fn: decimalSIPrefix('V', 1) }, - { name: 'Millivolt (mV)', id: 'mvolt', fn: decimalSIPrefix('V', -1) }, - { name: 'Decibel-milliwatt (dBm)', id: 'dBm', fn: decimalSIPrefix('dBm') }, - { name: 'Ohm (Ω)', id: 'ohm', fn: decimalSIPrefix('Ω') }, - { name: 'Lumens (Lm)', id: 'lumens', fn: decimalSIPrefix('Lm') }, - ], - }, - { - name: 'flow', - formats: [ - { name: 'Gallons/min (gpm)', id: 'flowgpm', fn: toFixedUnit('gpm') }, - { name: 'Cubic meters/sec (cms)', id: 'flowcms', fn: toFixedUnit('cms') }, - { name: 'Cubic feet/sec (cfs)', id: 'flowcfs', fn: toFixedUnit('cfs') }, - { name: 'Cubic feet/min (cfm)', id: 'flowcfm', fn: toFixedUnit('cfm') }, - { name: 'Litre/hour', id: 'litreh', fn: toFixedUnit('l/h') }, - { name: 'Litre/min (l/min)', id: 'flowlpm', fn: toFixedUnit('l/min') }, - { name: 'milliLitre/min (mL/min)', id: 'flowmlpm', fn: toFixedUnit('mL/min') }, - ], - }, - { - name: 'force', - formats: [ - { name: 'Newton-meters (Nm)', id: 'forceNm', fn: decimalSIPrefix('Nm') }, - { name: 'Kilonewton-meters (kNm)', id: 'forcekNm', fn: decimalSIPrefix('Nm', 1) }, - { name: 'Newtons (N)', id: 'forceN', fn: decimalSIPrefix('N') }, - { name: 'Kilonewtons (kN)', id: 'forcekN', fn: decimalSIPrefix('N', 1) }, - ], - }, - { - name: 'hash rate', - formats: [ - { name: 'hashes/sec', id: 'Hs', fn: decimalSIPrefix('H/s') }, - { name: 'kilohashes/sec', id: 'KHs', fn: decimalSIPrefix('H/s', 1) }, - { name: 'megahashes/sec', id: 'MHs', fn: decimalSIPrefix('H/s', 2) }, - { name: 'gigahashes/sec', id: 'GHs', fn: decimalSIPrefix('H/s', 3) }, - { name: 'terahashes/sec', id: 'THs', fn: decimalSIPrefix('H/s', 4) }, - { name: 'petahashes/sec', id: 'PHs', fn: decimalSIPrefix('H/s', 5) }, - { name: 'exahashes/sec', id: 'EHs', fn: decimalSIPrefix('H/s', 6) }, - ], - }, - { - name: 'mass', - formats: [ - { name: 'milligram (mg)', id: 'massmg', fn: decimalSIPrefix('g', -1) }, - { name: 'gram (g)', id: 'massg', fn: decimalSIPrefix('g') }, - { name: 'kilogram (kg)', id: 'masskg', fn: decimalSIPrefix('g', 1) }, - { name: 'metric ton (t)', id: 'masst', fn: toFixedUnit('t') }, - ], - }, - { - name: 'length', - formats: [ - { name: 'millimetre (mm)', id: 'lengthmm', fn: decimalSIPrefix('m', -1) }, - { name: 'feet (ft)', id: 'lengthft', fn: toFixedUnit('ft') }, - { name: 'meter (m)', id: 'lengthm', fn: decimalSIPrefix('m') }, - { name: 'kilometer (km)', id: 'lengthkm', fn: decimalSIPrefix('m', 1) }, - { name: 'mile (mi)', id: 'lengthmi', fn: toFixedUnit('mi') }, - ], - }, - { - name: 'pressure', - formats: [ - { name: 'Millibars', id: 'pressurembar', fn: decimalSIPrefix('bar', -1) }, - { name: 'Bars', id: 'pressurebar', fn: decimalSIPrefix('bar') }, - { name: 'Kilobars', id: 'pressurekbar', fn: decimalSIPrefix('bar', 1) }, - { name: 'Hectopascals', id: 'pressurehpa', fn: toFixedUnit('hPa') }, - { name: 'Kilopascals', id: 'pressurekpa', fn: toFixedUnit('kPa') }, - { name: 'Inches of mercury', id: 'pressurehg', fn: toFixedUnit('"Hg') }, - { name: 'PSI', id: 'pressurepsi', fn: scaledUnits(1000, ['psi', 'ksi', 'Mpsi']) }, - ], - }, - { - name: 'radiation', - formats: [ - { name: 'Becquerel (Bq)', id: 'radbq', fn: decimalSIPrefix('Bq') }, - { name: 'curie (Ci)', id: 'radci', fn: decimalSIPrefix('Ci') }, - { name: 'Gray (Gy)', id: 'radgy', fn: decimalSIPrefix('Gy') }, - { name: 'rad', id: 'radrad', fn: decimalSIPrefix('rad') }, - { name: 'Sievert (Sv)', id: 'radsv', fn: decimalSIPrefix('Sv') }, - { name: 'rem', id: 'radrem', fn: decimalSIPrefix('rem') }, - { name: 'Exposure (C/kg)', id: 'radexpckg', fn: decimalSIPrefix('C/kg') }, - { name: 'roentgen (R)', id: 'radr', fn: decimalSIPrefix('R') }, - { name: 'Sievert/hour (Sv/h)', id: 'radsvh', fn: decimalSIPrefix('Sv/h') }, - ], - }, - { - name: 'temperature', - formats: [ - { name: 'Celsius (°C)', id: 'celsius', fn: toFixedUnit('°C') }, - { name: 'Farenheit (°F)', id: 'farenheit', fn: toFixedUnit('°F') }, - { name: 'Kelvin (K)', id: 'kelvin', fn: toFixedUnit('K') }, - ], - }, - { - name: 'time', - formats: [ - { name: 'Hertz (1/s)', id: 'hertz', fn: decimalSIPrefix('Hz') }, - { name: 'nanoseconds (ns)', id: 'ns', fn: toNanoSeconds }, - { name: 'microseconds (µs)', id: 'µs', fn: toMicroSeconds }, - { name: 'milliseconds (ms)', id: 'ms', fn: toMilliSeconds }, - { name: 'seconds (s)', id: 's', fn: toSeconds }, - { name: 'minutes (m)', id: 'm', fn: toMinutes }, - { name: 'hours (h)', id: 'h', fn: toHours }, - { name: 'days (d)', id: 'd', fn: toDays }, - { name: 'duration (ms)', id: 'dtdurationms', fn: toDurationInMilliseconds }, - { name: 'duration (s)', id: 'dtdurations', fn: toDurationInSeconds }, - { name: 'duration (hh:mm:ss)', id: 'dthms', fn: toDurationInHoursMinutesSeconds }, - { name: 'Timeticks (s/100)', id: 'timeticks', fn: toTimeTicks }, - { name: 'clock (ms)', id: 'clockms', fn: toClockMilliseconds }, - { name: 'clock (s)', id: 'clocks', fn: toClockSeconds }, - ], - }, - { - name: 'throughput', - formats: [ - { name: 'ops/sec (ops)', id: 'ops', fn: decimalSIPrefix('ops') }, - { name: 'requests/sec (rps)', id: 'reqps', fn: decimalSIPrefix('reqps') }, - { name: 'reads/sec (rps)', id: 'rps', fn: decimalSIPrefix('rps') }, - { name: 'writes/sec (wps)', id: 'wps', fn: decimalSIPrefix('wps') }, - { name: 'I/O ops/sec (iops)', id: 'iops', fn: decimalSIPrefix('iops') }, - { name: 'ops/min (opm)', id: 'opm', fn: decimalSIPrefix('opm') }, - { name: 'reads/min (rpm)', id: 'rpm', fn: decimalSIPrefix('rpm') }, - { name: 'writes/min (wpm)', id: 'wpm', fn: decimalSIPrefix('wpm') }, - ], - }, - { - name: 'volume', - formats: [ - { name: 'millilitre (mL)', id: 'mlitre', fn: decimalSIPrefix('L', -1) }, - { name: 'litre (L)', id: 'litre', fn: decimalSIPrefix('L') }, - { name: 'cubic metre', id: 'm3', fn: toFixedUnit('m³') }, - { name: 'Normal cubic metre', id: 'Nm3', fn: toFixedUnit('Nm³') }, - { name: 'cubic decimetre', id: 'dm3', fn: toFixedUnit('dm³') }, - { name: 'gallons', id: 'gallons', fn: toFixedUnit('gal') }, - ], - }, - ]; - - for (const cat of categories) { - for (const format of cat.formats) { - index[format.id] = format.fn; - } - } - - hasBuildIndex = true; -} - -export function getValueFormat(id: string): ValueFormatter { - if (!hasBuildIndex) { - buildFormats(); - } - - return index[id]; -} - -export function getValueFormatterIndex(): ValueFormatterIndex { - if (!hasBuildIndex) { - buildFormats(); - } - - return index; -} - -export function getUnitFormats() { - if (!hasBuildIndex) { - buildFormats(); - } - - return categories.map(cat => { - return { - text: cat.name, - submenu: cat.formats.map(format => { - return { - text: format.name, - id: format.id, - }; - }), - }; - }); -} From 38dcbeb2fd01eca4e0d4612e1a5d547021cc0fd7 Mon Sep 17 00:00:00 2001 From: Peter Holmberg Date: Tue, 8 Jan 2019 15:56:01 +0100 Subject: [PATCH 06/26] removing duplicated things --- .../src/utils/ValueFormats/categories.ts | 18 +- .../ValueFormats/symbolFormatters.test.ts | 4 - .../src/utils/ValueFormats/valueFormats.ts | 12 + public/app/core/utils/kbn.ts | 936 +----------------- 4 files changed, 23 insertions(+), 947 deletions(-) diff --git a/packages/grafana-ui/src/utils/ValueFormats/categories.ts b/packages/grafana-ui/src/utils/ValueFormats/categories.ts index 15ea8d15468..98739343beb 100644 --- a/packages/grafana-ui/src/utils/ValueFormats/categories.ts +++ b/packages/grafana-ui/src/utils/ValueFormats/categories.ts @@ -1,4 +1,4 @@ -import { locale, scaledUnits, toFixed, toFixedUnit, ValueFormatCategory } from './valueFormats'; +import { locale, scaledUnits, simpleCountUnit, toFixed, toFixedUnit, ValueFormatCategory } from './valueFormats'; import { dateTimeAsIso, dateTimeAsUS, @@ -289,14 +289,14 @@ export const getCategories = (): ValueFormatCategory[] => [ { name: 'throughput', formats: [ - { name: 'ops/sec (ops)', id: 'ops', fn: decimalSIPrefix('ops') }, - { name: 'requests/sec (rps)', id: 'reqps', fn: decimalSIPrefix('reqps') }, - { name: 'reads/sec (rps)', id: 'rps', fn: decimalSIPrefix('rps') }, - { name: 'writes/sec (wps)', id: 'wps', fn: decimalSIPrefix('wps') }, - { name: 'I/O ops/sec (iops)', id: 'iops', fn: decimalSIPrefix('iops') }, - { name: 'ops/min (opm)', id: 'opm', fn: decimalSIPrefix('opm') }, - { name: 'reads/min (rpm)', id: 'rpm', fn: decimalSIPrefix('rpm') }, - { name: 'writes/min (wpm)', id: 'wpm', fn: decimalSIPrefix('wpm') }, + { name: 'ops/sec (ops)', id: 'ops', fn: simpleCountUnit('ops') }, + { name: 'requests/sec (rps)', id: 'reqps', fn: simpleCountUnit('reqps') }, + { name: 'reads/sec (rps)', id: 'rps', fn: simpleCountUnit('rps') }, + { name: 'writes/sec (wps)', id: 'wps', fn: simpleCountUnit('wps') }, + { name: 'I/O ops/sec (iops)', id: 'iops', fn: simpleCountUnit('iops') }, + { name: 'ops/min (opm)', id: 'opm', fn: simpleCountUnit('opm') }, + { name: 'reads/min (rpm)', id: 'rpm', fn: simpleCountUnit('rpm') }, + { name: 'writes/min (wpm)', id: 'wpm', fn: simpleCountUnit('wpm') }, ], }, { diff --git a/packages/grafana-ui/src/utils/ValueFormats/symbolFormatters.test.ts b/packages/grafana-ui/src/utils/ValueFormats/symbolFormatters.test.ts index 6b91a49c0dc..49278711608 100644 --- a/packages/grafana-ui/src/utils/ValueFormats/symbolFormatters.test.ts +++ b/packages/grafana-ui/src/utils/ValueFormats/symbolFormatters.test.ts @@ -4,8 +4,4 @@ describe('Currency', () => { it('should format as usd', () => { expect(currency('$')(1532.82, 1, -1)).toEqual('$1.53K'); }); - - it('should format as usd', () => { - expect(currency('kr')(1532.82, 1, -1)).toEqual('1.53K kr'); - }); }); diff --git a/packages/grafana-ui/src/utils/ValueFormats/valueFormats.ts b/packages/grafana-ui/src/utils/ValueFormats/valueFormats.ts index 152f242b1a8..e70a4d186c7 100644 --- a/packages/grafana-ui/src/utils/ValueFormats/valueFormats.ts +++ b/packages/grafana-ui/src/utils/ValueFormats/valueFormats.ts @@ -107,6 +107,18 @@ export function locale(value: number, decimals: number) { return value.toLocaleString(undefined, { maximumFractionDigits: decimals }); } +export function simpleCountUnit(symbol: string) { + const units = ['', 'K', 'M', 'B', 'T']; + const scaler = scaledUnits(1000, units); + return (size: number, decimals: number, scaledDecimals: number) => { + if (size === null) { + return ''; + } + const scaled = scaler(size, decimals, scaledDecimals); + return scaled + ' ' + symbol; + }; +} + function buildFormats() { categories = getCategories(); diff --git a/public/app/core/utils/kbn.ts b/public/app/core/utils/kbn.ts index 2fae6300d16..20088c60f66 100644 --- a/public/app/core/utils/kbn.ts +++ b/public/app/core/utils/kbn.ts @@ -1,6 +1,6 @@ import _ from 'lodash'; -import moment from 'moment'; import { getValueFormat, getValueFormatterIndex } from '@grafana/ui'; +import { getUnitFormats } from '@grafana/ui/src'; const kbn: any = {}; @@ -281,942 +281,10 @@ kbn.roundValue = (num, decimals) => { return Math.round(parseFloat(formatted)) / n; }; -///// FORMAT FUNCTION CONSTRUCTORS ///// - -kbn.formatBuilders = {}; - -// Formatter which always appends a fixed unit string to the value. No -// scaling of the value is performed. -kbn.formatBuilders.fixedUnit = unit => { - return (size, decimals) => { - if (size === null) { - return ''; - } - return kbn.toFixed(size, decimals) + ' ' + unit; - }; -}; - -// Formatter which scales the unit string geometrically according to the given -// numeric factor. Repeatedly scales the value down by the factor until it is -// less than the factor in magnitude, or the end of the array is reached. -kbn.formatBuilders.scaledUnits = (factor, extArray) => { - return (size, decimals, scaledDecimals) => { - if (size === null) { - return ''; - } - - let steps = 0; - const limit = extArray.length; - - while (Math.abs(size) >= factor) { - steps++; - size /= factor; - - if (steps >= limit) { - return 'NA'; - } - } - - if (steps > 0 && scaledDecimals !== null) { - decimals = scaledDecimals + 3 * steps; - } - - return kbn.toFixed(size, decimals) + extArray[steps]; - }; -}; - -// Extension of the scaledUnits builder which uses SI decimal prefixes. If an -// offset is given, it adjusts the starting units at the given prefix; a value -// of 0 starts at no scale; -3 drops to nano, +2 starts at mega, etc. -kbn.formatBuilders.decimalSIPrefix = (unit, offset) => { - let prefixes = ['n', 'µ', 'm', '', 'k', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y']; - prefixes = prefixes.slice(3 + (offset || 0)); - const units = prefixes.map(p => { - return ' ' + p + unit; - }); - return kbn.formatBuilders.scaledUnits(1000, units); -}; - -// Extension of the scaledUnits builder which uses SI binary prefixes. If -// offset is given, it starts the units at the given prefix; otherwise, the -// offset defaults to zero and the initial unit is not prefixed. -kbn.formatBuilders.binarySIPrefix = (unit, offset) => { - const prefixes = ['', 'Ki', 'Mi', 'Gi', 'Ti', 'Pi', 'Ei', 'Zi', 'Yi'].slice(offset); - const units = prefixes.map(p => { - return ' ' + p + unit; - }); - return kbn.formatBuilders.scaledUnits(1024, units); -}; - -// Currency formatter for prefixing a symbol onto a number. Supports scaling -// up to the trillions. -kbn.formatBuilders.currency = symbol => { - const units = ['', 'K', 'M', 'B', 'T']; - const scaler = kbn.formatBuilders.scaledUnits(1000, units); - return (size, decimals, scaledDecimals) => { - if (size === null) { - return ''; - } - const scaled = scaler(size, decimals, scaledDecimals); - return symbol + scaled; - }; -}; - -kbn.formatBuilders.simpleCountUnit = symbol => { - const units = ['', 'K', 'M', 'B', 'T']; - const scaler = kbn.formatBuilders.scaledUnits(1000, units); - return (size, decimals, scaledDecimals) => { - if (size === null) { - return ''; - } - const scaled = scaler(size, decimals, scaledDecimals); - return scaled + ' ' + symbol; - }; -}; - -///// VALUE FORMATS ///// - -// Dimensionless Units -kbn.valueFormats.none = kbn.toFixed; -kbn.valueFormats.short = kbn.formatBuilders.scaledUnits(1000, [ - '', - ' K', - ' Mil', - ' Bil', - ' Tri', - ' Quadr', - ' Quint', - ' Sext', - ' Sept', -]); -kbn.valueFormats.dB = kbn.formatBuilders.fixedUnit('dB'); - -kbn.valueFormats.percent = (size, decimals) => { - if (size === null) { - return ''; - } - return kbn.toFixed(size, decimals) + '%'; -}; - -kbn.valueFormats.percentunit = (size, decimals) => { - if (size === null) { - return ''; - } - return kbn.toFixed(100 * size, decimals) + '%'; -}; - -/* Formats the value to hex. Uses float if specified decimals are not 0. - * There are two submenu - * , one with 0x, and one without */ - -kbn.valueFormats.hex = (value, decimals) => { - if (value == null) { - return ''; - } - return parseFloat(kbn.toFixed(value, decimals)) - .toString(16) - .toUpperCase(); -}; - -kbn.valueFormats.hex0x = (value, decimals) => { - if (value == null) { - return ''; - } - const hexString = kbn.valueFormats.hex(value, decimals); - if (hexString.substring(0, 1) === '-') { - return '-0x' + hexString.substring(1); - } - return '0x' + hexString; -}; - -kbn.valueFormats.sci = (value, decimals) => { - if (value == null) { - return ''; - } - return value.toExponential(decimals); -}; - -kbn.valueFormats.locale = (value, decimals) => { - if (value == null) { - return ''; - } - return value.toLocaleString(undefined, { maximumFractionDigits: decimals }); -}; - -// Currencies -kbn.valueFormats.currencyUSD = kbn.formatBuilders.currency('$'); -kbn.valueFormats.currencyGBP = kbn.formatBuilders.currency('£'); -kbn.valueFormats.currencyEUR = kbn.formatBuilders.currency('€'); -kbn.valueFormats.currencyJPY = kbn.formatBuilders.currency('¥'); -kbn.valueFormats.currencyRUB = kbn.formatBuilders.currency('₽'); -kbn.valueFormats.currencyUAH = kbn.formatBuilders.currency('₴'); -kbn.valueFormats.currencyBRL = kbn.formatBuilders.currency('R$'); -kbn.valueFormats.currencyDKK = kbn.formatBuilders.currency('kr'); -kbn.valueFormats.currencyISK = kbn.formatBuilders.currency('kr'); -kbn.valueFormats.currencyNOK = kbn.formatBuilders.currency('kr'); -kbn.valueFormats.currencySEK = kbn.formatBuilders.currency('kr'); -kbn.valueFormats.currencyCZK = kbn.formatBuilders.currency('czk'); -kbn.valueFormats.currencyCHF = kbn.formatBuilders.currency('CHF'); -kbn.valueFormats.currencyPLN = kbn.formatBuilders.currency('zł'); -kbn.valueFormats.currencyBTC = kbn.formatBuilders.currency('฿'); - -// Data (Binary) -kbn.valueFormats.bits = kbn.formatBuilders.binarySIPrefix('b'); -kbn.valueFormats.bytes = kbn.formatBuilders.binarySIPrefix('B'); -kbn.valueFormats.kbytes = kbn.formatBuilders.binarySIPrefix('B', 1); -kbn.valueFormats.mbytes = kbn.formatBuilders.binarySIPrefix('B', 2); -kbn.valueFormats.gbytes = kbn.formatBuilders.binarySIPrefix('B', 3); - -// Data (Decimal) -kbn.valueFormats.decbits = kbn.formatBuilders.decimalSIPrefix('b'); -kbn.valueFormats.decbytes = kbn.formatBuilders.decimalSIPrefix('B'); -kbn.valueFormats.deckbytes = kbn.formatBuilders.decimalSIPrefix('B', 1); -kbn.valueFormats.decmbytes = kbn.formatBuilders.decimalSIPrefix('B', 2); -kbn.valueFormats.decgbytes = kbn.formatBuilders.decimalSIPrefix('B', 3); - -// Data Rate -kbn.valueFormats.pps = kbn.formatBuilders.decimalSIPrefix('pps'); -kbn.valueFormats.bps = kbn.formatBuilders.decimalSIPrefix('bps'); -kbn.valueFormats.Bps = kbn.formatBuilders.decimalSIPrefix('B/s'); -kbn.valueFormats.KBs = kbn.formatBuilders.decimalSIPrefix('Bs', 1); -kbn.valueFormats.Kbits = kbn.formatBuilders.decimalSIPrefix('bps', 1); -kbn.valueFormats.MBs = kbn.formatBuilders.decimalSIPrefix('Bs', 2); -kbn.valueFormats.Mbits = kbn.formatBuilders.decimalSIPrefix('bps', 2); -kbn.valueFormats.GBs = kbn.formatBuilders.decimalSIPrefix('Bs', 3); -kbn.valueFormats.Gbits = kbn.formatBuilders.decimalSIPrefix('bps', 3); - -// Floating Point Operations per Second -kbn.valueFormats.flops = kbn.formatBuilders.decimalSIPrefix('FLOP/s'); -kbn.valueFormats.mflops = kbn.formatBuilders.decimalSIPrefix('FLOP/s', 2); -kbn.valueFormats.gflops = kbn.formatBuilders.decimalSIPrefix('FLOP/s', 3); -kbn.valueFormats.tflops = kbn.formatBuilders.decimalSIPrefix('FLOP/s', 4); -kbn.valueFormats.pflops = kbn.formatBuilders.decimalSIPrefix('FLOP/s', 5); -kbn.valueFormats.eflops = kbn.formatBuilders.decimalSIPrefix('FLOP/s', 6); - -// Hash Rate -kbn.valueFormats.Hs = kbn.formatBuilders.decimalSIPrefix('H/s'); -kbn.valueFormats.KHs = kbn.formatBuilders.decimalSIPrefix('H/s', 1); -kbn.valueFormats.MHs = kbn.formatBuilders.decimalSIPrefix('H/s', 2); -kbn.valueFormats.GHs = kbn.formatBuilders.decimalSIPrefix('H/s', 3); -kbn.valueFormats.THs = kbn.formatBuilders.decimalSIPrefix('H/s', 4); -kbn.valueFormats.PHs = kbn.formatBuilders.decimalSIPrefix('H/s', 5); -kbn.valueFormats.EHs = kbn.formatBuilders.decimalSIPrefix('H/s', 6); - -// Throughput -kbn.valueFormats.ops = kbn.formatBuilders.simpleCountUnit('ops'); -kbn.valueFormats.reqps = kbn.formatBuilders.simpleCountUnit('reqps'); -kbn.valueFormats.rps = kbn.formatBuilders.simpleCountUnit('rps'); -kbn.valueFormats.wps = kbn.formatBuilders.simpleCountUnit('wps'); -kbn.valueFormats.iops = kbn.formatBuilders.simpleCountUnit('iops'); -kbn.valueFormats.opm = kbn.formatBuilders.simpleCountUnit('opm'); -kbn.valueFormats.rpm = kbn.formatBuilders.simpleCountUnit('rpm'); -kbn.valueFormats.wpm = kbn.formatBuilders.simpleCountUnit('wpm'); - -// Energy -kbn.valueFormats.watt = kbn.formatBuilders.decimalSIPrefix('W'); -kbn.valueFormats.kwatt = kbn.formatBuilders.decimalSIPrefix('W', 1); -kbn.valueFormats.mwatt = kbn.formatBuilders.decimalSIPrefix('W', -1); -kbn.valueFormats.kwattm = kbn.formatBuilders.decimalSIPrefix('W/Min', 1); -kbn.valueFormats.Wm2 = kbn.formatBuilders.fixedUnit('W/m²'); -kbn.valueFormats.voltamp = kbn.formatBuilders.decimalSIPrefix('VA'); -kbn.valueFormats.kvoltamp = kbn.formatBuilders.decimalSIPrefix('VA', 1); -kbn.valueFormats.voltampreact = kbn.formatBuilders.decimalSIPrefix('var'); -kbn.valueFormats.kvoltampreact = kbn.formatBuilders.decimalSIPrefix('var', 1); -kbn.valueFormats.watth = kbn.formatBuilders.decimalSIPrefix('Wh'); -kbn.valueFormats.kwatth = kbn.formatBuilders.decimalSIPrefix('Wh', 1); -kbn.valueFormats.joule = kbn.formatBuilders.decimalSIPrefix('J'); -kbn.valueFormats.ev = kbn.formatBuilders.decimalSIPrefix('eV'); -kbn.valueFormats.amp = kbn.formatBuilders.decimalSIPrefix('A'); -kbn.valueFormats.kamp = kbn.formatBuilders.decimalSIPrefix('A', 1); -kbn.valueFormats.mamp = kbn.formatBuilders.decimalSIPrefix('A', -1); -kbn.valueFormats.volt = kbn.formatBuilders.decimalSIPrefix('V'); -kbn.valueFormats.kvolt = kbn.formatBuilders.decimalSIPrefix('V', 1); -kbn.valueFormats.mvolt = kbn.formatBuilders.decimalSIPrefix('V', -1); -kbn.valueFormats.dBm = kbn.formatBuilders.decimalSIPrefix('dBm'); -kbn.valueFormats.ohm = kbn.formatBuilders.decimalSIPrefix('Ω'); -kbn.valueFormats.lumens = kbn.formatBuilders.decimalSIPrefix('Lm'); - -// Temperature -kbn.valueFormats.celsius = kbn.formatBuilders.fixedUnit('°C'); -kbn.valueFormats.farenheit = kbn.formatBuilders.fixedUnit('°F'); -kbn.valueFormats.kelvin = kbn.formatBuilders.fixedUnit('K'); -kbn.valueFormats.humidity = kbn.formatBuilders.fixedUnit('%H'); - -// Pressure -kbn.valueFormats.pressurebar = kbn.formatBuilders.decimalSIPrefix('bar'); -kbn.valueFormats.pressurembar = kbn.formatBuilders.decimalSIPrefix('bar', -1); -kbn.valueFormats.pressurekbar = kbn.formatBuilders.decimalSIPrefix('bar', 1); -kbn.valueFormats.pressurehpa = kbn.formatBuilders.fixedUnit('hPa'); -kbn.valueFormats.pressurekpa = kbn.formatBuilders.fixedUnit('kPa'); -kbn.valueFormats.pressurehg = kbn.formatBuilders.fixedUnit('"Hg'); -kbn.valueFormats.pressurepsi = kbn.formatBuilders.scaledUnits(1000, [' psi', ' ksi', ' Mpsi']); - -// Force -kbn.valueFormats.forceNm = kbn.formatBuilders.decimalSIPrefix('Nm'); -kbn.valueFormats.forcekNm = kbn.formatBuilders.decimalSIPrefix('Nm', 1); -kbn.valueFormats.forceN = kbn.formatBuilders.decimalSIPrefix('N'); -kbn.valueFormats.forcekN = kbn.formatBuilders.decimalSIPrefix('N', 1); - -// Length -kbn.valueFormats.lengthm = kbn.formatBuilders.decimalSIPrefix('m'); -kbn.valueFormats.lengthmm = kbn.formatBuilders.decimalSIPrefix('m', -1); -kbn.valueFormats.lengthkm = kbn.formatBuilders.decimalSIPrefix('m', 1); -kbn.valueFormats.lengthmi = kbn.formatBuilders.fixedUnit('mi'); -kbn.valueFormats.lengthft = kbn.formatBuilders.fixedUnit('ft'); - -// Area -kbn.valueFormats.areaM2 = kbn.formatBuilders.fixedUnit('m²'); -kbn.valueFormats.areaF2 = kbn.formatBuilders.fixedUnit('ft²'); -kbn.valueFormats.areaMI2 = kbn.formatBuilders.fixedUnit('mi²'); - -// Mass -kbn.valueFormats.massmg = kbn.formatBuilders.decimalSIPrefix('g', -1); -kbn.valueFormats.massg = kbn.formatBuilders.decimalSIPrefix('g'); -kbn.valueFormats.masskg = kbn.formatBuilders.decimalSIPrefix('g', 1); -kbn.valueFormats.masst = kbn.formatBuilders.fixedUnit('t'); - -// Velocity -kbn.valueFormats.velocityms = kbn.formatBuilders.fixedUnit('m/s'); -kbn.valueFormats.velocitykmh = kbn.formatBuilders.fixedUnit('km/h'); -kbn.valueFormats.velocitymph = kbn.formatBuilders.fixedUnit('mph'); -kbn.valueFormats.velocityknot = kbn.formatBuilders.fixedUnit('kn'); - -// Acceleration -kbn.valueFormats.accMS2 = kbn.formatBuilders.fixedUnit('m/sec²'); -kbn.valueFormats.accFS2 = kbn.formatBuilders.fixedUnit('f/sec²'); -kbn.valueFormats.accG = kbn.formatBuilders.fixedUnit('g'); - -// Volume -kbn.valueFormats.litre = kbn.formatBuilders.decimalSIPrefix('L'); -kbn.valueFormats.mlitre = kbn.formatBuilders.decimalSIPrefix('L', -1); -kbn.valueFormats.m3 = kbn.formatBuilders.fixedUnit('m³'); -kbn.valueFormats.Nm3 = kbn.formatBuilders.fixedUnit('Nm³'); -kbn.valueFormats.dm3 = kbn.formatBuilders.fixedUnit('dm³'); -kbn.valueFormats.gallons = kbn.formatBuilders.fixedUnit('gal'); - -// Flow -kbn.valueFormats.flowgpm = kbn.formatBuilders.fixedUnit('gpm'); -kbn.valueFormats.flowcms = kbn.formatBuilders.fixedUnit('cms'); -kbn.valueFormats.flowcfs = kbn.formatBuilders.fixedUnit('cfs'); -kbn.valueFormats.flowcfm = kbn.formatBuilders.fixedUnit('cfm'); -kbn.valueFormats.litreh = kbn.formatBuilders.fixedUnit('l/h'); -kbn.valueFormats.flowlpm = kbn.formatBuilders.fixedUnit('l/min'); -kbn.valueFormats.flowmlpm = kbn.formatBuilders.fixedUnit('mL/min'); - -// Angle -kbn.valueFormats.degree = kbn.formatBuilders.fixedUnit('°'); -kbn.valueFormats.radian = kbn.formatBuilders.fixedUnit('rad'); -kbn.valueFormats.grad = kbn.formatBuilders.fixedUnit('grad'); - -// Radiation -kbn.valueFormats.radbq = kbn.formatBuilders.decimalSIPrefix('Bq'); -kbn.valueFormats.radci = kbn.formatBuilders.decimalSIPrefix('Ci'); -kbn.valueFormats.radgy = kbn.formatBuilders.decimalSIPrefix('Gy'); -kbn.valueFormats.radrad = kbn.formatBuilders.decimalSIPrefix('rad'); -kbn.valueFormats.radsv = kbn.formatBuilders.decimalSIPrefix('Sv'); -kbn.valueFormats.radrem = kbn.formatBuilders.decimalSIPrefix('rem'); -kbn.valueFormats.radexpckg = kbn.formatBuilders.decimalSIPrefix('C/kg'); -kbn.valueFormats.radr = kbn.formatBuilders.decimalSIPrefix('R'); -kbn.valueFormats.radsvh = kbn.formatBuilders.decimalSIPrefix('Sv/h'); - -// Concentration -kbn.valueFormats.ppm = kbn.formatBuilders.fixedUnit('ppm'); -kbn.valueFormats.conppb = kbn.formatBuilders.fixedUnit('ppb'); -kbn.valueFormats.conngm3 = kbn.formatBuilders.fixedUnit('ng/m³'); -kbn.valueFormats.conngNm3 = kbn.formatBuilders.fixedUnit('ng/Nm³'); -kbn.valueFormats.conμgm3 = kbn.formatBuilders.fixedUnit('μg/m³'); -kbn.valueFormats.conμgNm3 = kbn.formatBuilders.fixedUnit('μg/Nm³'); -kbn.valueFormats.conmgm3 = kbn.formatBuilders.fixedUnit('mg/m³'); -kbn.valueFormats.conmgNm3 = kbn.formatBuilders.fixedUnit('mg/Nm³'); -kbn.valueFormats.congm3 = kbn.formatBuilders.fixedUnit('g/m³'); -kbn.valueFormats.congNm3 = kbn.formatBuilders.fixedUnit('g/Nm³'); -kbn.valueFormats.conmgdL = kbn.formatBuilders.fixedUnit('mg/dL'); -kbn.valueFormats.conmmolL = kbn.formatBuilders.fixedUnit('mmol/L'); - -// Time -kbn.valueFormats.hertz = kbn.formatBuilders.decimalSIPrefix('Hz'); - -kbn.valueFormats.ms = (size, decimals, scaledDecimals) => { - if (size === null) { - return ''; - } - - if (Math.abs(size) < 1000) { - return kbn.toFixed(size, decimals) + ' ms'; - } else if (Math.abs(size) < 60000) { - // Less than 1 min - return kbn.toFixedScaled(size / 1000, decimals, scaledDecimals, 3, ' s'); - } else if (Math.abs(size) < 3600000) { - // Less than 1 hour, divide in minutes - return kbn.toFixedScaled(size / 60000, decimals, scaledDecimals, 5, ' min'); - } else if (Math.abs(size) < 86400000) { - // Less than one day, divide in hours - return kbn.toFixedScaled(size / 3600000, decimals, scaledDecimals, 7, ' hour'); - } else if (Math.abs(size) < 31536000000) { - // Less than one year, divide in days - return kbn.toFixedScaled(size / 86400000, decimals, scaledDecimals, 8, ' day'); - } - - return kbn.toFixedScaled(size / 31536000000, decimals, scaledDecimals, 10, ' year'); -}; - -kbn.valueFormats.s = (size, decimals, scaledDecimals) => { - if (size === null) { - return ''; - } - - // Less than 1 µs, divide in ns - if (Math.abs(size) < 0.000001) { - return kbn.toFixedScaled(size * 1e9, decimals, scaledDecimals - decimals, -9, ' ns'); - } - // Less than 1 ms, divide in µs - if (Math.abs(size) < 0.001) { - return kbn.toFixedScaled(size * 1e6, decimals, scaledDecimals - decimals, -6, ' µs'); - } - // Less than 1 second, divide in ms - if (Math.abs(size) < 1) { - return kbn.toFixedScaled(size * 1e3, decimals, scaledDecimals - decimals, -3, ' ms'); - } - - if (Math.abs(size) < 60) { - return kbn.toFixed(size, decimals) + ' s'; - } else if (Math.abs(size) < 3600) { - // Less than 1 hour, divide in minutes - return kbn.toFixedScaled(size / 60, decimals, scaledDecimals, 1, ' min'); - } else if (Math.abs(size) < 86400) { - // Less than one day, divide in hours - return kbn.toFixedScaled(size / 3600, decimals, scaledDecimals, 4, ' hour'); - } else if (Math.abs(size) < 604800) { - // Less than one week, divide in days - return kbn.toFixedScaled(size / 86400, decimals, scaledDecimals, 5, ' day'); - } else if (Math.abs(size) < 31536000) { - // Less than one year, divide in week - return kbn.toFixedScaled(size / 604800, decimals, scaledDecimals, 6, ' week'); - } - - return kbn.toFixedScaled(size / 3.15569e7, decimals, scaledDecimals, 7, ' year'); -}; - -kbn.valueFormats['µs'] = (size, decimals, scaledDecimals) => { - if (size === null) { - return ''; - } - - if (Math.abs(size) < 1000) { - return kbn.toFixed(size, decimals) + ' µs'; - } else if (Math.abs(size) < 1000000) { - return kbn.toFixedScaled(size / 1000, decimals, scaledDecimals, 3, ' ms'); - } else { - return kbn.toFixedScaled(size / 1000000, decimals, scaledDecimals, 6, ' s'); - } -}; - -kbn.valueFormats.ns = (size, decimals, scaledDecimals) => { - if (size === null) { - return ''; - } - - if (Math.abs(size) < 1000) { - return kbn.toFixed(size, decimals) + ' ns'; - } else if (Math.abs(size) < 1000000) { - return kbn.toFixedScaled(size / 1000, decimals, scaledDecimals, 3, ' µs'); - } else if (Math.abs(size) < 1000000000) { - return kbn.toFixedScaled(size / 1000000, decimals, scaledDecimals, 6, ' ms'); - } else if (Math.abs(size) < 60000000000) { - return kbn.toFixedScaled(size / 1000000000, decimals, scaledDecimals, 9, ' s'); - } else { - return kbn.toFixedScaled(size / 60000000000, decimals, scaledDecimals, 12, ' min'); - } -}; - -kbn.valueFormats.m = (size, decimals, scaledDecimals) => { - if (size === null) { - return ''; - } - - if (Math.abs(size) < 60) { - return kbn.toFixed(size, decimals) + ' min'; - } else if (Math.abs(size) < 1440) { - return kbn.toFixedScaled(size / 60, decimals, scaledDecimals, 2, ' hour'); - } else if (Math.abs(size) < 10080) { - return kbn.toFixedScaled(size / 1440, decimals, scaledDecimals, 3, ' day'); - } else if (Math.abs(size) < 604800) { - return kbn.toFixedScaled(size / 10080, decimals, scaledDecimals, 4, ' week'); - } else { - return kbn.toFixedScaled(size / 5.25948e5, decimals, scaledDecimals, 5, ' year'); - } -}; - -kbn.valueFormats.h = (size, decimals, scaledDecimals) => { - if (size === null) { - return ''; - } - - if (Math.abs(size) < 24) { - return kbn.toFixed(size, decimals) + ' hour'; - } else if (Math.abs(size) < 168) { - return kbn.toFixedScaled(size / 24, decimals, scaledDecimals, 2, ' day'); - } else if (Math.abs(size) < 8760) { - return kbn.toFixedScaled(size / 168, decimals, scaledDecimals, 3, ' week'); - } else { - return kbn.toFixedScaled(size / 8760, decimals, scaledDecimals, 4, ' year'); - } -}; - -kbn.valueFormats.d = (size, decimals, scaledDecimals) => { - if (size === null) { - return ''; - } - - if (Math.abs(size) < 7) { - return kbn.toFixed(size, decimals) + ' day'; - } else if (Math.abs(size) < 365) { - return kbn.toFixedScaled(size / 7, decimals, scaledDecimals, 2, ' week'); - } else { - return kbn.toFixedScaled(size / 365, decimals, scaledDecimals, 3, ' year'); - } -}; - -kbn.toDuration = (size, decimals, timeScale) => { - if (size === null) { - return ''; - } - if (size === 0) { - return '0 ' + timeScale + 's'; - } - if (size < 0) { - return kbn.toDuration(-size, decimals, timeScale) + ' ago'; - } - - const units = [ - { short: 'y', long: 'year' }, - { short: 'M', long: 'month' }, - { short: 'w', long: 'week' }, - { short: 'd', long: 'day' }, - { short: 'h', long: 'hour' }, - { short: 'm', long: 'minute' }, - { short: 's', long: 'second' }, - { short: 'ms', long: 'millisecond' }, - ]; - // convert $size to milliseconds - // intervals_in_seconds uses seconds (duh), convert them to milliseconds here to minimize floating point errors - size *= - kbn.intervals_in_seconds[ - units.find(e => { - return e.long === timeScale; - }).short - ] * 1000; - - const strings = []; - // after first value >= 1 print only $decimals more - let decrementDecimals = false; - for (let i = 0; i < units.length && decimals >= 0; i++) { - const interval = kbn.intervals_in_seconds[units[i].short] * 1000; - const value = size / interval; - if (value >= 1 || decrementDecimals) { - decrementDecimals = true; - const floor = Math.floor(value); - const unit = units[i].long + (floor !== 1 ? 's' : ''); - strings.push(floor + ' ' + unit); - size = size % interval; - decimals--; - } - } - - return strings.join(', '); -}; - -kbn.toClock = (size, decimals) => { - if (size === null) { - return ''; - } - - // < 1 second - if (size < 1000) { - return moment.utc(size).format('SSS\\m\\s'); - } - - // < 1 minute - if (size < 60000) { - let format = 'ss\\s:SSS\\m\\s'; - if (decimals === 0) { - format = 'ss\\s'; - } - return moment.utc(size).format(format); - } - - // < 1 hour - if (size < 3600000) { - let format = 'mm\\m:ss\\s:SSS\\m\\s'; - if (decimals === 0) { - format = 'mm\\m'; - } else if (decimals === 1) { - format = 'mm\\m:ss\\s'; - } - return moment.utc(size).format(format); - } - - let format = 'mm\\m:ss\\s:SSS\\m\\s'; - - const hours = `${('0' + Math.floor(moment.duration(size, 'milliseconds').asHours())).slice(-2)}h`; - - if (decimals === 0) { - format = ''; - } else if (decimals === 1) { - format = 'mm\\m'; - } else if (decimals === 2) { - format = 'mm\\m:ss\\s'; - } - - return format ? `${hours}:${moment.utc(size).format(format)}` : hours; -}; - -kbn.valueFormats.dtdurationms = (size, decimals) => { - return kbn.toDuration(size, decimals, 'millisecond'); -}; - -kbn.valueFormats.dtdurations = (size, decimals) => { - return kbn.toDuration(size, decimals, 'second'); -}; - -kbn.valueFormats.dthms = (size, decimals) => { - return kbn.secondsToHhmmss(size); -}; - -kbn.valueFormats.timeticks = (size, decimals, scaledDecimals) => { - return kbn.valueFormats.s(size / 100, decimals, scaledDecimals); -}; - -kbn.valueFormats.clockms = (size, decimals) => { - return kbn.toClock(size, decimals); -}; - -kbn.valueFormats.clocks = (size, decimals) => { - return kbn.toClock(size * 1000, decimals); -}; - -kbn.valueFormats.dateTimeAsIso = (epoch, isUtc) => { - const time = isUtc ? moment.utc(epoch) : moment(epoch); - - if (moment().isSame(epoch, 'day')) { - return time.format('HH:mm:ss'); - } - return time.format('YYYY-MM-DD HH:mm:ss'); -}; - -kbn.valueFormats.dateTimeAsUS = (epoch, isUtc) => { - const time = isUtc ? moment.utc(epoch) : moment(epoch); - - if (moment().isSame(epoch, 'day')) { - return time.format('h:mm:ss a'); - } - return time.format('MM/DD/YYYY h:mm:ss a'); -}; - -kbn.valueFormats.dateTimeFromNow = (epoch, isUtc) => { - const time = isUtc ? moment.utc(epoch) : moment(epoch); - return time.fromNow(); -}; - ///// FORMAT MENU ///// kbn.getUnitFormats = () => { - return [ - { - text: 'none', - submenu: [ - { text: 'none', value: 'none' }, - { text: 'short', value: 'short' }, - { text: 'percent (0-100)', value: 'percent' }, - { text: 'percent (0.0-1.0)', value: 'percentunit' }, - { text: 'Humidity (%H)', value: 'humidity' }, - { text: 'decibel', value: 'dB' }, - { text: 'hexadecimal (0x)', value: 'hex0x' }, - { text: 'hexadecimal', value: 'hex' }, - { text: 'scientific notation', value: 'sci' }, - { text: 'locale format', value: 'locale' }, - ], - }, - { - text: 'currency', - submenu: [ - { text: 'Dollars ($)', value: 'currencyUSD' }, - { text: 'Pounds (£)', value: 'currencyGBP' }, - { text: 'Euro (€)', value: 'currencyEUR' }, - { text: 'Yen (¥)', value: 'currencyJPY' }, - { text: 'Rubles (₽)', value: 'currencyRUB' }, - { text: 'Hryvnias (₴)', value: 'currencyUAH' }, - { text: 'Real (R$)', value: 'currencyBRL' }, - { text: 'Danish Krone (kr)', value: 'currencyDKK' }, - { text: 'Icelandic Króna (kr)', value: 'currencyISK' }, - { text: 'Norwegian Krone (kr)', value: 'currencyNOK' }, - { text: 'Swedish Krona (kr)', value: 'currencySEK' }, - { text: 'Czech koruna (czk)', value: 'currencyCZK' }, - { text: 'Swiss franc (CHF)', value: 'currencyCHF' }, - { text: 'Polish Złoty (PLN)', value: 'currencyPLN' }, - { text: 'Bitcoin (฿)', value: 'currencyBTC' }, - ], - }, - { - text: 'time', - submenu: [ - { text: 'Hertz (1/s)', value: 'hertz' }, - { text: 'nanoseconds (ns)', value: 'ns' }, - { text: 'microseconds (µs)', value: 'µs' }, - { text: 'milliseconds (ms)', value: 'ms' }, - { text: 'seconds (s)', value: 's' }, - { text: 'minutes (m)', value: 'm' }, - { text: 'hours (h)', value: 'h' }, - { text: 'days (d)', value: 'd' }, - { text: 'duration (ms)', value: 'dtdurationms' }, - { text: 'duration (s)', value: 'dtdurations' }, - { text: 'duration (hh:mm:ss)', value: 'dthms' }, - { text: 'Timeticks (s/100)', value: 'timeticks' }, - { text: 'clock (ms)', value: 'clockms' }, - { text: 'clock (s)', value: 'clocks' }, - ], - }, - { - text: 'date & time', - submenu: [ - { text: 'YYYY-MM-DD HH:mm:ss', value: 'dateTimeAsIso' }, - { text: 'DD/MM/YYYY h:mm:ss a', value: 'dateTimeAsUS' }, - { text: 'From Now', value: 'dateTimeFromNow' }, - ], - }, - { - text: 'data (IEC)', - submenu: [ - { text: 'bits', value: 'bits' }, - { text: 'bytes', value: 'bytes' }, - { text: 'kibibytes', value: 'kbytes' }, - { text: 'mebibytes', value: 'mbytes' }, - { text: 'gibibytes', value: 'gbytes' }, - ], - }, - { - text: 'data (Metric)', - submenu: [ - { text: 'bits', value: 'decbits' }, - { text: 'bytes', value: 'decbytes' }, - { text: 'kilobytes', value: 'deckbytes' }, - { text: 'megabytes', value: 'decmbytes' }, - { text: 'gigabytes', value: 'decgbytes' }, - ], - }, - { - text: 'data rate', - submenu: [ - { text: 'packets/sec', value: 'pps' }, - { text: 'bits/sec', value: 'bps' }, - { text: 'bytes/sec', value: 'Bps' }, - { text: 'kilobits/sec', value: 'Kbits' }, - { text: 'kilobytes/sec', value: 'KBs' }, - { text: 'megabits/sec', value: 'Mbits' }, - { text: 'megabytes/sec', value: 'MBs' }, - { text: 'gigabytes/sec', value: 'GBs' }, - { text: 'gigabits/sec', value: 'Gbits' }, - ], - }, - { - text: 'hash rate', - submenu: [ - { text: 'hashes/sec', value: 'Hs' }, - { text: 'kilohashes/sec', value: 'KHs' }, - { text: 'megahashes/sec', value: 'MHs' }, - { text: 'gigahashes/sec', value: 'GHs' }, - { text: 'terahashes/sec', value: 'THs' }, - { text: 'petahashes/sec', value: 'PHs' }, - { text: 'exahashes/sec', value: 'EHs' }, - ], - }, - { - text: 'computation throughput', - submenu: [ - { text: 'FLOP/s', value: 'flops' }, - { text: 'MFLOP/s', value: 'mflops' }, - { text: 'GFLOP/s', value: 'gflops' }, - { text: 'TFLOP/s', value: 'tflops' }, - { text: 'PFLOP/s', value: 'pflops' }, - { text: 'EFLOP/s', value: 'eflops' }, - ], - }, - { - text: 'throughput', - submenu: [ - { text: 'ops/sec (ops)', value: 'ops' }, - { text: 'requests/sec (rps)', value: 'reqps' }, - { text: 'reads/sec (rps)', value: 'rps' }, - { text: 'writes/sec (wps)', value: 'wps' }, - { text: 'I/O ops/sec (iops)', value: 'iops' }, - { text: 'ops/min (opm)', value: 'opm' }, - { text: 'reads/min (rpm)', value: 'rpm' }, - { text: 'writes/min (wpm)', value: 'wpm' }, - ], - }, - { - text: 'length', - submenu: [ - { text: 'millimetre (mm)', value: 'lengthmm' }, - { text: 'meter (m)', value: 'lengthm' }, - { text: 'feet (ft)', value: 'lengthft' }, - { text: 'kilometer (km)', value: 'lengthkm' }, - { text: 'mile (mi)', value: 'lengthmi' }, - ], - }, - { - text: 'area', - submenu: [ - { text: 'Square Meters (m²)', value: 'areaM2' }, - { text: 'Square Feet (ft²)', value: 'areaF2' }, - { text: 'Square Miles (mi²)', value: 'areaMI2' }, - ], - }, - { - text: 'mass', - submenu: [ - { text: 'milligram (mg)', value: 'massmg' }, - { text: 'gram (g)', value: 'massg' }, - { text: 'kilogram (kg)', value: 'masskg' }, - { text: 'metric ton (t)', value: 'masst' }, - ], - }, - { - text: 'velocity', - submenu: [ - { text: 'metres/second (m/s)', value: 'velocityms' }, - { text: 'kilometers/hour (km/h)', value: 'velocitykmh' }, - { text: 'miles/hour (mph)', value: 'velocitymph' }, - { text: 'knot (kn)', value: 'velocityknot' }, - ], - }, - { - text: 'volume', - submenu: [ - { text: 'millilitre (mL)', value: 'mlitre' }, - { text: 'litre (L)', value: 'litre' }, - { text: 'cubic metre', value: 'm3' }, - { text: 'Normal cubic metre', value: 'Nm3' }, - { text: 'cubic decimetre', value: 'dm3' }, - { text: 'gallons', value: 'gallons' }, - ], - }, - { - text: 'energy', - submenu: [ - { text: 'Watt (W)', value: 'watt' }, - { text: 'Kilowatt (kW)', value: 'kwatt' }, - { text: 'Milliwatt (mW)', value: 'mwatt' }, - { text: 'Watt per square meter (W/m²)', value: 'Wm2' }, - { text: 'Volt-ampere (VA)', value: 'voltamp' }, - { text: 'Kilovolt-ampere (kVA)', value: 'kvoltamp' }, - { text: 'Volt-ampere reactive (var)', value: 'voltampreact' }, - { text: 'Kilovolt-ampere reactive (kvar)', value: 'kvoltampreact' }, - { text: 'Watt-hour (Wh)', value: 'watth' }, - { text: 'Kilowatt-hour (kWh)', value: 'kwatth' }, - { text: 'Kilowatt-min (kWm)', value: 'kwattm' }, - { text: 'Joule (J)', value: 'joule' }, - { text: 'Electron volt (eV)', value: 'ev' }, - { text: 'Ampere (A)', value: 'amp' }, - { text: 'Kiloampere (kA)', value: 'kamp' }, - { text: 'Milliampere (mA)', value: 'mamp' }, - { text: 'Volt (V)', value: 'volt' }, - { text: 'Kilovolt (kV)', value: 'kvolt' }, - { text: 'Millivolt (mV)', value: 'mvolt' }, - { text: 'Decibel-milliwatt (dBm)', value: 'dBm' }, - { text: 'Ohm (Ω)', value: 'ohm' }, - { text: 'Lumens (Lm)', value: 'lumens' }, - ], - }, - { - text: 'temperature', - submenu: [ - { text: 'Celsius (°C)', value: 'celsius' }, - { text: 'Farenheit (°F)', value: 'farenheit' }, - { text: 'Kelvin (K)', value: 'kelvin' }, - ], - }, - { - text: 'pressure', - submenu: [ - { text: 'Millibars', value: 'pressurembar' }, - { text: 'Bars', value: 'pressurebar' }, - { text: 'Kilobars', value: 'pressurekbar' }, - { text: 'Hectopascals', value: 'pressurehpa' }, - { text: 'Kilopascals', value: 'pressurekpa' }, - { text: 'Inches of mercury', value: 'pressurehg' }, - { text: 'PSI', value: 'pressurepsi' }, - ], - }, - { - text: 'force', - submenu: [ - { text: 'Newton-meters (Nm)', value: 'forceNm' }, - { text: 'Kilonewton-meters (kNm)', value: 'forcekNm' }, - { text: 'Newtons (N)', value: 'forceN' }, - { text: 'Kilonewtons (kN)', value: 'forcekN' }, - ], - }, - { - text: 'flow', - submenu: [ - { text: 'Gallons/min (gpm)', value: 'flowgpm' }, - { text: 'Cubic meters/sec (cms)', value: 'flowcms' }, - { text: 'Cubic feet/sec (cfs)', value: 'flowcfs' }, - { text: 'Cubic feet/min (cfm)', value: 'flowcfm' }, - { text: 'Litre/hour', value: 'litreh' }, - { text: 'Litre/min (l/min)', value: 'flowlpm' }, - { text: 'milliLitre/min (mL/min)', value: 'flowmlpm' }, - ], - }, - { - text: 'angle', - submenu: [ - { text: 'Degrees (°)', value: 'degree' }, - { text: 'Radians', value: 'radian' }, - { text: 'Gradian', value: 'grad' }, - ], - }, - { - text: 'acceleration', - submenu: [ - { text: 'Meters/sec²', value: 'accMS2' }, - { text: 'Feet/sec²', value: 'accFS2' }, - { text: 'G unit', value: 'accG' }, - ], - }, - { - text: 'radiation', - submenu: [ - { text: 'Becquerel (Bq)', value: 'radbq' }, - { text: 'curie (Ci)', value: 'radci' }, - { text: 'Gray (Gy)', value: 'radgy' }, - { text: 'rad', value: 'radrad' }, - { text: 'Sievert (Sv)', value: 'radsv' }, - { text: 'rem', value: 'radrem' }, - { text: 'Exposure (C/kg)', value: 'radexpckg' }, - { text: 'roentgen (R)', value: 'radr' }, - { text: 'Sievert/hour (Sv/h)', value: 'radsvh' }, - ], - }, - { - text: 'concentration', - submenu: [ - { text: 'parts-per-million (ppm)', value: 'ppm' }, - { text: 'parts-per-billion (ppb)', value: 'conppb' }, - { text: 'nanogram per cubic meter (ng/m³)', value: 'conngm3' }, - { text: 'nanogram per normal cubic meter (ng/Nm³)', value: 'conngNm3' }, - { text: 'microgram per cubic meter (μg/m³)', value: 'conμgm3' }, - { text: 'microgram per normal cubic meter (μg/Nm³)', value: 'conμgNm3' }, - { text: 'milligram per cubic meter (mg/m³)', value: 'conmgm3' }, - { text: 'milligram per normal cubic meter (mg/Nm³)', value: 'conmgNm3' }, - { text: 'gram per cubic meter (g/m³)', value: 'congm3' }, - { text: 'gram per normal cubic meter (g/Nm³)', value: 'congNm3' }, - { text: 'milligrams per decilitre (mg/dL)', value: 'conmgdL' }, - { text: 'millimoles per litre (mmol/L)', value: 'conmmolL' }, - ], - }, - ]; + return getUnitFormats(); }; if (typeof Proxy !== "undefined") { From 78fe2db5e5264d04c143f0ae136f7eb137991128 Mon Sep 17 00:00:00 2001 From: Peter Holmberg Date: Wed, 9 Jan 2019 21:35:01 +0000 Subject: [PATCH 07/26] removing tests --- public/app/core/specs/kbn.test.ts | 493 ------------------------------ 1 file changed, 493 deletions(-) delete mode 100644 public/app/core/specs/kbn.test.ts diff --git a/public/app/core/specs/kbn.test.ts b/public/app/core/specs/kbn.test.ts deleted file mode 100644 index e621cdef632..00000000000 --- a/public/app/core/specs/kbn.test.ts +++ /dev/null @@ -1,493 +0,0 @@ -import kbn from '../utils/kbn'; -import * as dateMath from '../utils/datemath'; -import moment from 'moment'; - -describe('unit format menu', () => { - const menu = kbn.getUnitFormats(); - menu.map(submenu => { - describe('submenu ' + submenu.text, () => { - it('should have a title', () => { - expect(typeof submenu.text).toBe('string'); - }); - - it('should have a submenu', () => { - expect(Array.isArray(submenu.submenu)).toBe(true); - }); - - submenu.submenu.map(entry => { - describe('entry ' + entry.text, () => { - it('should have a title', () => { - expect(typeof entry.text).toBe('string'); - }); - it('should have a format', () => { - expect(typeof entry.value).toBe('string'); - }); - it('should have a valid format', () => { - expect(typeof kbn.valueFormats[entry.value]).toBe('function'); - }); - }); - }); - }); - }); -}); - -function describeValueFormat(desc, value, tickSize, tickDecimals, result) { - describe('value format: ' + desc, () => { - it('should translate ' + value + ' as ' + result, () => { - const scaledDecimals = tickDecimals - Math.floor(Math.log(tickSize) / Math.LN10); - const str = kbn.valueFormats[desc](value, tickDecimals, scaledDecimals); - expect(str).toBe(result); - }); - }); -} - -describeValueFormat('ms', 0.0024, 0.0005, 4, '0.0024 ms'); -describeValueFormat('ms', 100, 1, 0, '100 ms'); -describeValueFormat('ms', 1250, 10, 0, '1.25 s'); -describeValueFormat('ms', 1250, 300, 0, '1.3 s'); -describeValueFormat('ms', 65150, 10000, 0, '1.1 min'); -describeValueFormat('ms', 6515000, 1500000, 0, '1.8 hour'); -describeValueFormat('ms', 651500000, 150000000, 0, '8 day'); - -describeValueFormat('none', 2.75e-10, 0, 10, '3e-10'); -describeValueFormat('none', 0, 0, 2, '0'); -describeValueFormat('dB', 10, 1000, 2, '10.00 dB'); - -describeValueFormat('percent', 0, 0, 0, '0%'); -describeValueFormat('percent', 53, 0, 1, '53.0%'); -describeValueFormat('percentunit', 0.0, 0, 0, '0%'); -describeValueFormat('percentunit', 0.278, 0, 1, '27.8%'); -describeValueFormat('percentunit', 1.0, 0, 0, '100%'); - -describeValueFormat('currencyUSD', 7.42, 10000, 2, '$7.42'); -describeValueFormat('currencyUSD', 1532.82, 1000, 1, '$1.53K'); -describeValueFormat('currencyUSD', 18520408.7, 10000000, 0, '$19M'); - -describeValueFormat('bytes', -1.57e308, -1.57e308, 2, 'NA'); - -describeValueFormat('ns', 25, 1, 0, '25 ns'); -describeValueFormat('ns', 2558, 50, 0, '2.56 µs'); - -describeValueFormat('ops', 123, 1, 0, '123 ops'); -describeValueFormat('rps', 456000, 1000, -1, '456K rps'); -describeValueFormat('rps', 123456789, 1000000, 2, '123.457M rps'); -describeValueFormat('wps', 789000000, 1000000, -1, '789M wps'); -describeValueFormat('iops', 11000000000, 1000000000, -1, '11B iops'); - -describeValueFormat('s', 1.23456789e-7, 1e-10, 8, '123.5 ns'); -describeValueFormat('s', 1.23456789e-4, 1e-7, 5, '123.5 µs'); -describeValueFormat('s', 1.23456789e-3, 1e-6, 4, '1.235 ms'); -describeValueFormat('s', 1.23456789e-2, 1e-5, 3, '12.35 ms'); -describeValueFormat('s', 1.23456789e-1, 1e-4, 2, '123.5 ms'); -describeValueFormat('s', 24, 1, 0, '24 s'); -describeValueFormat('s', 246, 1, 0, '4.1 min'); -describeValueFormat('s', 24567, 100, 0, '6.82 hour'); -describeValueFormat('s', 24567890, 10000, 0, '40.62 week'); -describeValueFormat('s', 24567890000, 1000000, 0, '778.53 year'); - -describeValueFormat('m', 24, 1, 0, '24 min'); -describeValueFormat('m', 246, 10, 0, '4.1 hour'); -describeValueFormat('m', 6545, 10, 0, '4.55 day'); -describeValueFormat('m', 24567, 100, 0, '2.44 week'); -describeValueFormat('m', 24567892, 10000, 0, '46.7 year'); - -describeValueFormat('h', 21, 1, 0, '21 hour'); -describeValueFormat('h', 145, 1, 0, '6.04 day'); -describeValueFormat('h', 1234, 100, 0, '7.3 week'); -describeValueFormat('h', 9458, 1000, 0, '1.08 year'); - -describeValueFormat('d', 3, 1, 0, '3 day'); -describeValueFormat('d', 245, 100, 0, '35 week'); -describeValueFormat('d', 2456, 10, 0, '6.73 year'); - -describe('date time formats', () => { - const epoch = 1505634997920; - const utcTime = moment.utc(epoch); - const browserTime = moment(epoch); - - it('should format as iso date', () => { - const expected = browserTime.format('YYYY-MM-DD HH:mm:ss'); - const actual = kbn.valueFormats.dateTimeAsIso(epoch); - expect(actual).toBe(expected); - }); - - it('should format as iso date (in UTC)', () => { - const expected = utcTime.format('YYYY-MM-DD HH:mm:ss'); - const actual = kbn.valueFormats.dateTimeAsIso(epoch, true); - expect(actual).toBe(expected); - }); - - it('should format as iso date and skip date when today', () => { - const now = moment(); - const expected = now.format('HH:mm:ss'); - const actual = kbn.valueFormats.dateTimeAsIso(now.valueOf(), false); - expect(actual).toBe(expected); - }); - - it('should format as iso date (in UTC) and skip date when today', () => { - const now = moment.utc(); - const expected = now.format('HH:mm:ss'); - const actual = kbn.valueFormats.dateTimeAsIso(now.valueOf(), true); - expect(actual).toBe(expected); - }); - - it('should format as US date', () => { - const expected = browserTime.format('MM/DD/YYYY h:mm:ss a'); - const actual = kbn.valueFormats.dateTimeAsUS(epoch, false); - expect(actual).toBe(expected); - }); - - it('should format as US date (in UTC)', () => { - const expected = utcTime.format('MM/DD/YYYY h:mm:ss a'); - const actual = kbn.valueFormats.dateTimeAsUS(epoch, true); - expect(actual).toBe(expected); - }); - - it('should format as US date and skip date when today', () => { - const now = moment(); - const expected = now.format('h:mm:ss a'); - const actual = kbn.valueFormats.dateTimeAsUS(now.valueOf(), false); - expect(actual).toBe(expected); - }); - - it('should format as US date (in UTC) and skip date when today', () => { - const now = moment.utc(); - const expected = now.format('h:mm:ss a'); - const actual = kbn.valueFormats.dateTimeAsUS(now.valueOf(), true); - expect(actual).toBe(expected); - }); - - it('should format as from now with days', () => { - const daysAgo = moment().add(-7, 'd'); - const expected = '7 days ago'; - const actual = kbn.valueFormats.dateTimeFromNow(daysAgo.valueOf(), false); - expect(actual).toBe(expected); - }); - - it('should format as from now with days (in UTC)', () => { - const daysAgo = moment.utc().add(-7, 'd'); - const expected = '7 days ago'; - const actual = kbn.valueFormats.dateTimeFromNow(daysAgo.valueOf(), true); - expect(actual).toBe(expected); - }); - - it('should format as from now with minutes', () => { - const daysAgo = moment().add(-2, 'm'); - const expected = '2 minutes ago'; - const actual = kbn.valueFormats.dateTimeFromNow(daysAgo.valueOf(), false); - expect(actual).toBe(expected); - }); - - it('should format as from now with minutes (in UTC)', () => { - const daysAgo = moment.utc().add(-2, 'm'); - const expected = '2 minutes ago'; - const actual = kbn.valueFormats.dateTimeFromNow(daysAgo.valueOf(), true); - expect(actual).toBe(expected); - }); -}); - -describe('kbn.toFixed and negative decimals', () => { - it('should treat as zero decimals', () => { - const str = kbn.toFixed(186.123, -2); - expect(str).toBe('186'); - }); -}); - -describe('kbn ms format when scaled decimals is null do not use it', () => { - it('should use specified decimals', () => { - const str = kbn.valueFormats['ms'](10000086.123, 1, null); - expect(str).toBe('2.8 hour'); - }); -}); - -describe('kbn kbytes format when scaled decimals is null do not use it', () => { - it('should use specified decimals', () => { - const str = kbn.valueFormats['kbytes'](10000000, 3, null); - expect(str).toBe('9.537 GiB'); - }); -}); - -describe('kbn deckbytes format when scaled decimals is null do not use it', () => { - it('should use specified decimals', () => { - const str = kbn.valueFormats['deckbytes'](10000000, 3, null); - expect(str).toBe('10.000 GB'); - }); -}); - -describe('kbn roundValue', () => { - it('should should handle null value', () => { - const str = kbn.roundValue(null, 2); - expect(str).toBe(null); - }); - it('should round value', () => { - const str = kbn.roundValue(200.877, 2); - expect(str).toBe(200.88); - }); -}); - -describe('calculateInterval', () => { - it('1h 100 resultion', () => { - const range = { from: dateMath.parse('now-1h'), to: dateMath.parse('now') }; - const res = kbn.calculateInterval(range, 100, null); - expect(res.interval).toBe('30s'); - }); - - it('10m 1600 resolution', () => { - const range = { from: dateMath.parse('now-10m'), to: dateMath.parse('now') }; - const res = kbn.calculateInterval(range, 1600, null); - expect(res.interval).toBe('500ms'); - expect(res.intervalMs).toBe(500); - }); - - it('fixed user min interval', () => { - const range = { from: dateMath.parse('now-10m'), to: dateMath.parse('now') }; - const res = kbn.calculateInterval(range, 1600, '10s'); - expect(res.interval).toBe('10s'); - expect(res.intervalMs).toBe(10000); - }); - - it('short time range and user low limit', () => { - const range = { from: dateMath.parse('now-10m'), to: dateMath.parse('now') }; - const res = kbn.calculateInterval(range, 1600, '>10s'); - expect(res.interval).toBe('10s'); - }); - - it('large time range and user low limit', () => { - const range = { from: dateMath.parse('now-14d'), to: dateMath.parse('now') }; - const res = kbn.calculateInterval(range, 1000, '>10s'); - expect(res.interval).toBe('20m'); - }); - - it('10s 900 resolution and user low limit in ms', () => { - const range = { from: dateMath.parse('now-10s'), to: dateMath.parse('now') }; - const res = kbn.calculateInterval(range, 900, '>15ms'); - expect(res.interval).toBe('15ms'); - }); - - it('1d 1 resolution', () => { - const range = { from: dateMath.parse('now-1d'), to: dateMath.parse('now') }; - const res = kbn.calculateInterval(range, 1, null); - expect(res.interval).toBe('1d'); - expect(res.intervalMs).toBe(86400000); - }); - - it('86399s 1 resolution', () => { - const range = { - from: dateMath.parse('now-86390s'), - to: dateMath.parse('now'), - }; - const res = kbn.calculateInterval(range, 1, null); - expect(res.interval).toBe('12h'); - expect(res.intervalMs).toBe(43200000); - }); -}); - -describe('hex', () => { - it('positive integer', () => { - const str = kbn.valueFormats.hex(100, 0); - expect(str).toBe('64'); - }); - it('negative integer', () => { - const str = kbn.valueFormats.hex(-100, 0); - expect(str).toBe('-64'); - }); - it('null', () => { - const str = kbn.valueFormats.hex(null, 0); - expect(str).toBe(''); - }); - it('positive float', () => { - const str = kbn.valueFormats.hex(50.52, 1); - expect(str).toBe('32.8'); - }); - it('negative float', () => { - const str = kbn.valueFormats.hex(-50.333, 2); - expect(str).toBe('-32.547AE147AE14'); - }); -}); - -describe('hex 0x', () => { - it('positive integeter', () => { - const str = kbn.valueFormats.hex0x(7999, 0); - expect(str).toBe('0x1F3F'); - }); - it('negative integer', () => { - const str = kbn.valueFormats.hex0x(-584, 0); - expect(str).toBe('-0x248'); - }); - it('null', () => { - const str = kbn.valueFormats.hex0x(null, 0); - expect(str).toBe(''); - }); - it('positive float', () => { - const str = kbn.valueFormats.hex0x(74.443, 3); - expect(str).toBe('0x4A.716872B020C4'); - }); - it('negative float', () => { - const str = kbn.valueFormats.hex0x(-65.458, 1); - expect(str).toBe('-0x41.8'); - }); -}); - -describe('duration', () => { - it('null', () => { - const str = kbn.toDuration(null, 0, 'millisecond'); - expect(str).toBe(''); - }); - it('0 milliseconds', () => { - const str = kbn.toDuration(0, 0, 'millisecond'); - expect(str).toBe('0 milliseconds'); - }); - it('1 millisecond', () => { - const str = kbn.toDuration(1, 0, 'millisecond'); - expect(str).toBe('1 millisecond'); - }); - it('-1 millisecond', () => { - const str = kbn.toDuration(-1, 0, 'millisecond'); - expect(str).toBe('1 millisecond ago'); - }); - it('seconds', () => { - const str = kbn.toDuration(1, 0, 'second'); - expect(str).toBe('1 second'); - }); - it('minutes', () => { - const str = kbn.toDuration(1, 0, 'minute'); - expect(str).toBe('1 minute'); - }); - it('hours', () => { - const str = kbn.toDuration(1, 0, 'hour'); - expect(str).toBe('1 hour'); - }); - it('days', () => { - const str = kbn.toDuration(1, 0, 'day'); - expect(str).toBe('1 day'); - }); - it('weeks', () => { - const str = kbn.toDuration(1, 0, 'week'); - expect(str).toBe('1 week'); - }); - it('months', () => { - const str = kbn.toDuration(1, 0, 'month'); - expect(str).toBe('1 month'); - }); - it('years', () => { - const str = kbn.toDuration(1, 0, 'year'); - expect(str).toBe('1 year'); - }); - it('decimal days', () => { - const str = kbn.toDuration(1.5, 2, 'day'); - expect(str).toBe('1 day, 12 hours, 0 minutes'); - }); - it('decimal months', () => { - const str = kbn.toDuration(1.5, 3, 'month'); - expect(str).toBe('1 month, 2 weeks, 1 day, 0 hours'); - }); - it('no decimals', () => { - const str = kbn.toDuration(38898367008, 0, 'millisecond'); - expect(str).toBe('1 year'); - }); - it('1 decimal', () => { - const str = kbn.toDuration(38898367008, 1, 'millisecond'); - expect(str).toBe('1 year, 2 months'); - }); - it('too many decimals', () => { - const str = kbn.toDuration(38898367008, 20, 'millisecond'); - expect(str).toBe('1 year, 2 months, 3 weeks, 4 days, 5 hours, 6 minutes, 7 seconds, 8 milliseconds'); - }); - it('floating point error', () => { - const str = kbn.toDuration(36993906007, 8, 'millisecond'); - expect(str).toBe('1 year, 2 months, 0 weeks, 3 days, 4 hours, 5 minutes, 6 seconds, 7 milliseconds'); - }); -}); - -describe('clock', () => { - it('null', () => { - const str = kbn.toClock(null, 0); - expect(str).toBe(''); - }); - it('size less than 1 second', () => { - const str = kbn.toClock(999, 0); - expect(str).toBe('999ms'); - }); - describe('size less than 1 minute', () => { - it('default', () => { - const str = kbn.toClock(59999); - expect(str).toBe('59s:999ms'); - }); - it('decimals equals 0', () => { - const str = kbn.toClock(59999, 0); - expect(str).toBe('59s'); - }); - }); - describe('size less than 1 hour', () => { - it('default', () => { - const str = kbn.toClock(3599999); - expect(str).toBe('59m:59s:999ms'); - }); - it('decimals equals 0', () => { - const str = kbn.toClock(3599999, 0); - expect(str).toBe('59m'); - }); - it('decimals equals 1', () => { - const str = kbn.toClock(3599999, 1); - expect(str).toBe('59m:59s'); - }); - }); - describe('size greater than or equal 1 hour', () => { - it('default', () => { - const str = kbn.toClock(7199999); - expect(str).toBe('01h:59m:59s:999ms'); - }); - it('decimals equals 0', () => { - const str = kbn.toClock(7199999, 0); - expect(str).toBe('01h'); - }); - it('decimals equals 1', () => { - const str = kbn.toClock(7199999, 1); - expect(str).toBe('01h:59m'); - }); - it('decimals equals 2', () => { - const str = kbn.toClock(7199999, 2); - expect(str).toBe('01h:59m:59s'); - }); - }); - describe('size greater than or equal 1 day', () => { - it('default', () => { - const str = kbn.toClock(89999999); - expect(str).toBe('24h:59m:59s:999ms'); - }); - it('decimals equals 0', () => { - const str = kbn.toClock(89999999, 0); - expect(str).toBe('24h'); - }); - it('decimals equals 1', () => { - const str = kbn.toClock(89999999, 1); - expect(str).toBe('24h:59m'); - }); - it('decimals equals 2', () => { - const str = kbn.toClock(89999999, 2); - expect(str).toBe('24h:59m:59s'); - }); - }); -}); - -describe('volume', () => { - it('1000m3', () => { - const str = kbn.valueFormats['m3'](1000, 1, null); - expect(str).toBe('1000.0 m³'); - }); -}); - -describe('hh:mm:ss', () => { - it('00:04:06', () => { - const str = kbn.valueFormats['dthms'](246, 1); - expect(str).toBe('00:04:06'); - }); - it('24:00:00', () => { - const str = kbn.valueFormats['dthms'](86400, 1); - expect(str).toBe('24:00:00'); - }); - it('6824413:53:20', () => { - const str = kbn.valueFormats['dthms'](24567890000, 1); - expect(str).toBe('6824413:53:20'); - }); -}); From 13e6d2c5cb1c2d572b020055904a03861799218b Mon Sep 17 00:00:00 2001 From: Peter Holmberg Date: Wed, 9 Jan 2019 22:05:29 +0000 Subject: [PATCH 08/26] fixing unitpicker --- public/app/core/components/Select/UnitPicker.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/app/core/components/Select/UnitPicker.tsx b/public/app/core/components/Select/UnitPicker.tsx index 75885cbbb84..29fa2928045 100644 --- a/public/app/core/components/Select/UnitPicker.tsx +++ b/public/app/core/components/Select/UnitPicker.tsx @@ -23,7 +23,7 @@ export default class UnitPicker extends PureComponent { const options = group.submenu.map(unit => { return { label: unit.text, - value: unit.value, + value: unit.id, }; }); From 31d35a6884dcad78903fd2d6c490a62be9638c80 Mon Sep 17 00:00:00 2001 From: Peter Holmberg Date: Wed, 9 Jan 2019 22:21:42 +0000 Subject: [PATCH 09/26] rename --- .../grafana-ui/src/utils/ValueFormats/valueFormats.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/grafana-ui/src/utils/ValueFormats/valueFormats.ts b/packages/grafana-ui/src/utils/ValueFormats/valueFormats.ts index e70a4d186c7..ade0115fef2 100644 --- a/packages/grafana-ui/src/utils/ValueFormats/valueFormats.ts +++ b/packages/grafana-ui/src/utils/ValueFormats/valueFormats.ts @@ -20,7 +20,7 @@ interface ValueFormatterIndex { // Globals & formats cache let categories: ValueFormatCategory[] = []; const index: ValueFormatterIndex = {}; -let hasBuildIndex = false; +let hasBuiltIndex = false; export function toFixed(value: number, decimals?: number): string { if (value === null) { @@ -128,11 +128,11 @@ function buildFormats() { } } - hasBuildIndex = true; + hasBuiltIndex = true; } export function getValueFormat(id: string): ValueFormatter { - if (!hasBuildIndex) { + if (!hasBuiltIndex) { buildFormats(); } @@ -140,7 +140,7 @@ export function getValueFormat(id: string): ValueFormatter { } export function getValueFormatterIndex(): ValueFormatterIndex { - if (!hasBuildIndex) { + if (!hasBuiltIndex) { buildFormats(); } @@ -148,7 +148,7 @@ export function getValueFormatterIndex(): ValueFormatterIndex { } export function getUnitFormats() { - if (!hasBuildIndex) { + if (!hasBuiltIndex) { buildFormats(); } From 8aae6e8c09d726ae588dc6a7db1bafeb3b1d8762 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Fri, 11 Jan 2019 11:22:50 +0100 Subject: [PATCH 10/26] value formats: renamed folder --- packages/grafana-ui/src/utils/index.ts | 2 +- .../arithmeticFormatters.test.ts | 0 .../{ValueFormats => value_formats}/arithmeticFormatters.ts | 0 .../src/utils/{ValueFormats => value_formats}/categories.ts | 0 .../{ValueFormats => value_formats}/dateTimeFormatters.test.ts | 0 .../utils/{ValueFormats => value_formats}/dateTimeFormatters.ts | 0 .../{ValueFormats => value_formats}/symbolFormatters.test.ts | 0 .../utils/{ValueFormats => value_formats}/symbolFormatters.ts | 0 .../src/utils/{ValueFormats => value_formats}/valueFormats.ts | 2 +- 9 files changed, 2 insertions(+), 2 deletions(-) rename packages/grafana-ui/src/utils/{ValueFormats => value_formats}/arithmeticFormatters.test.ts (100%) rename packages/grafana-ui/src/utils/{ValueFormats => value_formats}/arithmeticFormatters.ts (100%) rename packages/grafana-ui/src/utils/{ValueFormats => value_formats}/categories.ts (100%) rename packages/grafana-ui/src/utils/{ValueFormats => value_formats}/dateTimeFormatters.test.ts (100%) rename packages/grafana-ui/src/utils/{ValueFormats => value_formats}/dateTimeFormatters.ts (100%) rename packages/grafana-ui/src/utils/{ValueFormats => value_formats}/symbolFormatters.test.ts (100%) rename packages/grafana-ui/src/utils/{ValueFormats => value_formats}/symbolFormatters.ts (100%) rename packages/grafana-ui/src/utils/{ValueFormats => value_formats}/valueFormats.ts (99%) diff --git a/packages/grafana-ui/src/utils/index.ts b/packages/grafana-ui/src/utils/index.ts index b1804c8605e..15694248832 100644 --- a/packages/grafana-ui/src/utils/index.ts +++ b/packages/grafana-ui/src/utils/index.ts @@ -1,2 +1,2 @@ export * from './processTimeSeries'; -export * from './ValueFormats/valueFormats'; +export * from './value_formats/valueFormats'; diff --git a/packages/grafana-ui/src/utils/ValueFormats/arithmeticFormatters.test.ts b/packages/grafana-ui/src/utils/value_formats/arithmeticFormatters.test.ts similarity index 100% rename from packages/grafana-ui/src/utils/ValueFormats/arithmeticFormatters.test.ts rename to packages/grafana-ui/src/utils/value_formats/arithmeticFormatters.test.ts diff --git a/packages/grafana-ui/src/utils/ValueFormats/arithmeticFormatters.ts b/packages/grafana-ui/src/utils/value_formats/arithmeticFormatters.ts similarity index 100% rename from packages/grafana-ui/src/utils/ValueFormats/arithmeticFormatters.ts rename to packages/grafana-ui/src/utils/value_formats/arithmeticFormatters.ts diff --git a/packages/grafana-ui/src/utils/ValueFormats/categories.ts b/packages/grafana-ui/src/utils/value_formats/categories.ts similarity index 100% rename from packages/grafana-ui/src/utils/ValueFormats/categories.ts rename to packages/grafana-ui/src/utils/value_formats/categories.ts diff --git a/packages/grafana-ui/src/utils/ValueFormats/dateTimeFormatters.test.ts b/packages/grafana-ui/src/utils/value_formats/dateTimeFormatters.test.ts similarity index 100% rename from packages/grafana-ui/src/utils/ValueFormats/dateTimeFormatters.test.ts rename to packages/grafana-ui/src/utils/value_formats/dateTimeFormatters.test.ts diff --git a/packages/grafana-ui/src/utils/ValueFormats/dateTimeFormatters.ts b/packages/grafana-ui/src/utils/value_formats/dateTimeFormatters.ts similarity index 100% rename from packages/grafana-ui/src/utils/ValueFormats/dateTimeFormatters.ts rename to packages/grafana-ui/src/utils/value_formats/dateTimeFormatters.ts diff --git a/packages/grafana-ui/src/utils/ValueFormats/symbolFormatters.test.ts b/packages/grafana-ui/src/utils/value_formats/symbolFormatters.test.ts similarity index 100% rename from packages/grafana-ui/src/utils/ValueFormats/symbolFormatters.test.ts rename to packages/grafana-ui/src/utils/value_formats/symbolFormatters.test.ts diff --git a/packages/grafana-ui/src/utils/ValueFormats/symbolFormatters.ts b/packages/grafana-ui/src/utils/value_formats/symbolFormatters.ts similarity index 100% rename from packages/grafana-ui/src/utils/ValueFormats/symbolFormatters.ts rename to packages/grafana-ui/src/utils/value_formats/symbolFormatters.ts diff --git a/packages/grafana-ui/src/utils/ValueFormats/valueFormats.ts b/packages/grafana-ui/src/utils/value_formats/valueFormats.ts similarity index 99% rename from packages/grafana-ui/src/utils/ValueFormats/valueFormats.ts rename to packages/grafana-ui/src/utils/value_formats/valueFormats.ts index ade0115fef2..dc06ce7848e 100644 --- a/packages/grafana-ui/src/utils/ValueFormats/valueFormats.ts +++ b/packages/grafana-ui/src/utils/value_formats/valueFormats.ts @@ -147,7 +147,7 @@ export function getValueFormatterIndex(): ValueFormatterIndex { return index; } -export function getUnitFormats() { +export function getValueFormats() { if (!hasBuiltIndex) { buildFormats(); } From 9e6411bf4bab47b2354042733b9e683895eed17f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Fri, 11 Jan 2019 13:31:25 +0100 Subject: [PATCH 11/26] value formats: another rename and updates code to use new valueFormats func --- packages/grafana-ui/src/utils/index.ts | 2 +- .../arithmeticFormatters.test.ts | 0 .../arithmeticFormatters.ts | 0 .../utils/{value_formats => valueFormats}/categories.ts | 0 .../dateTimeFormatters.test.ts | 0 .../{value_formats => valueFormats}/dateTimeFormatters.ts | 0 .../symbolFormatters.test.ts | 0 .../{value_formats => valueFormats}/symbolFormatters.ts | 0 .../utils/{value_formats => valueFormats}/valueFormats.ts | 2 +- public/app/core/components/Select/UnitPicker.tsx | 6 +++--- public/app/core/utils/kbn.ts | 8 +++++--- public/app/plugins/panel/graph/axes_editor.ts | 4 ++-- public/app/plugins/panel/table/column_options.ts | 4 ++-- 13 files changed, 14 insertions(+), 12 deletions(-) rename packages/grafana-ui/src/utils/{value_formats => valueFormats}/arithmeticFormatters.test.ts (100%) rename packages/grafana-ui/src/utils/{value_formats => valueFormats}/arithmeticFormatters.ts (100%) rename packages/grafana-ui/src/utils/{value_formats => valueFormats}/categories.ts (100%) rename packages/grafana-ui/src/utils/{value_formats => valueFormats}/dateTimeFormatters.test.ts (100%) rename packages/grafana-ui/src/utils/{value_formats => valueFormats}/dateTimeFormatters.ts (100%) rename packages/grafana-ui/src/utils/{value_formats => valueFormats}/symbolFormatters.test.ts (100%) rename packages/grafana-ui/src/utils/{value_formats => valueFormats}/symbolFormatters.ts (100%) rename packages/grafana-ui/src/utils/{value_formats => valueFormats}/valueFormats.ts (99%) diff --git a/packages/grafana-ui/src/utils/index.ts b/packages/grafana-ui/src/utils/index.ts index 15694248832..77940e19719 100644 --- a/packages/grafana-ui/src/utils/index.ts +++ b/packages/grafana-ui/src/utils/index.ts @@ -1,2 +1,2 @@ export * from './processTimeSeries'; -export * from './value_formats/valueFormats'; +export * from './valueFormats/valueFormats'; diff --git a/packages/grafana-ui/src/utils/value_formats/arithmeticFormatters.test.ts b/packages/grafana-ui/src/utils/valueFormats/arithmeticFormatters.test.ts similarity index 100% rename from packages/grafana-ui/src/utils/value_formats/arithmeticFormatters.test.ts rename to packages/grafana-ui/src/utils/valueFormats/arithmeticFormatters.test.ts diff --git a/packages/grafana-ui/src/utils/value_formats/arithmeticFormatters.ts b/packages/grafana-ui/src/utils/valueFormats/arithmeticFormatters.ts similarity index 100% rename from packages/grafana-ui/src/utils/value_formats/arithmeticFormatters.ts rename to packages/grafana-ui/src/utils/valueFormats/arithmeticFormatters.ts diff --git a/packages/grafana-ui/src/utils/value_formats/categories.ts b/packages/grafana-ui/src/utils/valueFormats/categories.ts similarity index 100% rename from packages/grafana-ui/src/utils/value_formats/categories.ts rename to packages/grafana-ui/src/utils/valueFormats/categories.ts diff --git a/packages/grafana-ui/src/utils/value_formats/dateTimeFormatters.test.ts b/packages/grafana-ui/src/utils/valueFormats/dateTimeFormatters.test.ts similarity index 100% rename from packages/grafana-ui/src/utils/value_formats/dateTimeFormatters.test.ts rename to packages/grafana-ui/src/utils/valueFormats/dateTimeFormatters.test.ts diff --git a/packages/grafana-ui/src/utils/value_formats/dateTimeFormatters.ts b/packages/grafana-ui/src/utils/valueFormats/dateTimeFormatters.ts similarity index 100% rename from packages/grafana-ui/src/utils/value_formats/dateTimeFormatters.ts rename to packages/grafana-ui/src/utils/valueFormats/dateTimeFormatters.ts diff --git a/packages/grafana-ui/src/utils/value_formats/symbolFormatters.test.ts b/packages/grafana-ui/src/utils/valueFormats/symbolFormatters.test.ts similarity index 100% rename from packages/grafana-ui/src/utils/value_formats/symbolFormatters.test.ts rename to packages/grafana-ui/src/utils/valueFormats/symbolFormatters.test.ts diff --git a/packages/grafana-ui/src/utils/value_formats/symbolFormatters.ts b/packages/grafana-ui/src/utils/valueFormats/symbolFormatters.ts similarity index 100% rename from packages/grafana-ui/src/utils/value_formats/symbolFormatters.ts rename to packages/grafana-ui/src/utils/valueFormats/symbolFormatters.ts diff --git a/packages/grafana-ui/src/utils/value_formats/valueFormats.ts b/packages/grafana-ui/src/utils/valueFormats/valueFormats.ts similarity index 99% rename from packages/grafana-ui/src/utils/value_formats/valueFormats.ts rename to packages/grafana-ui/src/utils/valueFormats/valueFormats.ts index dc06ce7848e..0a56ce58e5b 100644 --- a/packages/grafana-ui/src/utils/value_formats/valueFormats.ts +++ b/packages/grafana-ui/src/utils/valueFormats/valueFormats.ts @@ -158,7 +158,7 @@ export function getValueFormats() { submenu: cat.formats.map(format => { return { text: format.name, - id: format.id, + value: format.id, }; }), }; diff --git a/public/app/core/components/Select/UnitPicker.tsx b/public/app/core/components/Select/UnitPicker.tsx index 29fa2928045..0da8a148b9a 100644 --- a/public/app/core/components/Select/UnitPicker.tsx +++ b/public/app/core/components/Select/UnitPicker.tsx @@ -1,6 +1,6 @@ import React, { PureComponent } from 'react'; import Select from './Select'; -import kbn from 'app/core/utils/kbn'; +import { getValueFormats } from '@grafana/ui'; interface Props { onChange: (item: any) => void; @@ -16,14 +16,14 @@ export default class UnitPicker extends PureComponent { render() { const { defaultValue, onChange, width } = this.props; - const unitGroups = kbn.getUnitFormats(); + const unitGroups = getValueFormats(); // Need to transform the data structure to work well with Select const groupOptions = unitGroups.map(group => { const options = group.submenu.map(unit => { return { label: unit.text, - value: unit.id, + value: unit.value, }; }); diff --git a/public/app/core/utils/kbn.ts b/public/app/core/utils/kbn.ts index 20088c60f66..a3a96f8afc3 100644 --- a/public/app/core/utils/kbn.ts +++ b/public/app/core/utils/kbn.ts @@ -1,6 +1,5 @@ import _ from 'lodash'; -import { getValueFormat, getValueFormatterIndex } from '@grafana/ui'; -import { getUnitFormats } from '@grafana/ui/src'; +import { getValueFormat, getValueFormatterIndex, getValueFormats } from '@grafana/ui'; const kbn: any = {}; @@ -284,9 +283,12 @@ kbn.roundValue = (num, decimals) => { ///// FORMAT MENU ///// kbn.getUnitFormats = () => { - return getUnitFormats(); + return getValueFormats(); }; +// +// Backward compatible layer for value formats to support old plugins +// if (typeof Proxy !== "undefined") { kbn.valueFormats = new Proxy(kbn.valueFormats, { get(target, name, receiver) { diff --git a/public/app/plugins/panel/graph/axes_editor.ts b/public/app/plugins/panel/graph/axes_editor.ts index 3d2dd4acbc5..04ef62f16fb 100644 --- a/public/app/plugins/panel/graph/axes_editor.ts +++ b/public/app/plugins/panel/graph/axes_editor.ts @@ -1,4 +1,4 @@ -import kbn from 'app/core/utils/kbn'; +import { getValueFormats } from '@grafana/ui'; export class AxesEditorCtrl { panel: any; @@ -15,7 +15,7 @@ export class AxesEditorCtrl { this.panel = this.panelCtrl.panel; this.$scope.ctrl = this; - this.unitFormats = kbn.getUnitFormats(); + this.unitFormats = getValueFormats(); this.logScales = { linear: 1, diff --git a/public/app/plugins/panel/table/column_options.ts b/public/app/plugins/panel/table/column_options.ts index 4c810d9987d..dfe5ff8fdcd 100644 --- a/public/app/plugins/panel/table/column_options.ts +++ b/public/app/plugins/panel/table/column_options.ts @@ -1,5 +1,5 @@ import _ from 'lodash'; -import kbn from 'app/core/utils/kbn'; +import { getValueFormats } from '@grafana/ui'; export class ColumnOptionsCtrl { panel: any; @@ -22,7 +22,7 @@ export class ColumnOptionsCtrl { this.activeStyleIndex = 0; this.panelCtrl = $scope.ctrl; this.panel = this.panelCtrl.panel; - this.unitFormats = kbn.getUnitFormats(); + this.unitFormats = getValueFormats(); this.colorModes = [ { text: 'Disabled', value: null }, { text: 'Cell', value: 'cell' }, From c3fdc1a0fb5f92d0483181084e7bbfdccbaa68d3 Mon Sep 17 00:00:00 2001 From: Dominik Prokop Date: Fri, 11 Jan 2019 16:19:34 +0100 Subject: [PATCH 12/26] Panel time override tests --- package.json | 1 + .../features/dashboard/utils/panel.test.ts | 68 +++++++++++++++++++ yarn.lock | 5 ++ 3 files changed, 74 insertions(+) create mode 100644 public/app/features/dashboard/utils/panel.test.ts diff --git a/package.json b/package.json index c8d891b91bc..470101ff0c4 100644 --- a/package.json +++ b/package.json @@ -64,6 +64,7 @@ "html-webpack-plugin": "^3.2.0", "husky": "^0.14.3", "jest": "^23.6.0", + "jest-date-mock": "^1.0.6", "lint-staged": "^6.0.0", "load-grunt-tasks": "3.5.2", "mini-css-extract-plugin": "^0.4.0", diff --git a/public/app/features/dashboard/utils/panel.test.ts b/public/app/features/dashboard/utils/panel.test.ts new file mode 100644 index 00000000000..eeeb664f874 --- /dev/null +++ b/public/app/features/dashboard/utils/panel.test.ts @@ -0,0 +1,68 @@ +import moment from 'moment'; +import { TimeRange } from '@grafana/ui'; +import { applyPanelTimeOverrides } from 'app/features/dashboard/utils/panel'; +import { advanceTo, clear } from 'jest-date-mock'; + +const dashboardTimeRange: TimeRange = { + from: moment([2019, 1, 11, 12, 0]), + to: moment([2019, 1, 11, 18, 0]), + raw: { + from: 'now-6h', + to: 'now', + }, +}; + +describe('applyPanelTimeOverrides', () => { + const fakeCurrentDate = moment([2019, 1, 11, 14, 0, 0]).toDate(); + + beforeAll(() => { + advanceTo(fakeCurrentDate); + }); + + afterAll(() => { + clear(); + }); + + it('should apply relative time override', () => { + const panelModel = { + timeFrom: '2h', + }; + + // @ts-ignore: PanelModel type incositency + const overrides = applyPanelTimeOverrides(panelModel, dashboardTimeRange); + + expect(overrides.timeRange.from.toISOString()).toBe(moment([2019, 1, 11, 12]).toISOString()); + expect(overrides.timeRange.to.toISOString()).toBe(fakeCurrentDate.toISOString()); + }); + + it('should apply time shift', () => { + const panelModel = { + timeShift: '2h' + }; + + const expectedFromDate = moment([2019, 1, 11, 10, 0, 0]).toDate(); + const expectedToDate = moment([2019, 1, 11, 16, 0, 0]).toDate(); + + // @ts-ignore: PanelModel type incositency + const overrides = applyPanelTimeOverrides(panelModel, dashboardTimeRange); + + expect(overrides.timeRange.from.toISOString()).toBe(expectedFromDate.toISOString()); + expect(overrides.timeRange.to.toISOString()).toBe(expectedToDate.toISOString()); + }); + + it('should apply both relative time and time shift', () => { + const panelModel = { + timeFrom: '2h', + timeShift: '2h' + }; + + const expectedFromDate = moment([2019, 1, 11, 10, 0, 0]).toDate(); + const expectedToDate = moment([2019, 1, 11, 12, 0, 0]).toDate(); + + // @ts-ignore: PanelModel type incositency + const overrides = applyPanelTimeOverrides(panelModel, dashboardTimeRange); + + expect(overrides.timeRange.from.toISOString()).toBe(expectedFromDate.toISOString()); + expect(overrides.timeRange.to.toISOString()).toBe(expectedToDate.toISOString()); + }); +}); diff --git a/yarn.lock b/yarn.lock index 376a0b1d23a..70a2a93b8dd 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8153,6 +8153,11 @@ jest-config@^23.6.0: micromatch "^2.3.11" pretty-format "^23.6.0" +jest-date-mock@^1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/jest-date-mock/-/jest-date-mock-1.0.6.tgz#7ea405d1fa68f86bb727d12e47b9c5e6760066a6" + integrity sha512-wnLgDaK3i2md/cQ1wKx/+/78PieO4nkGen8avEmHd4dt1NGGxeuW8/oLAF5qsatQBXdn08pxpqRtUoDvTTLdRg== + jest-diff@^23.6.0: version "23.6.0" resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-23.6.0.tgz#1500f3f16e850bb3d71233408089be099f610c7d" From 7289e6e500f5ec09d6a2cb664b7cf23519b3a788 Mon Sep 17 00:00:00 2001 From: Dominik Prokop Date: Fri, 11 Jan 2019 16:49:29 +0100 Subject: [PATCH 13/26] Addedd assertions about raw time range when panel time overriden --- public/app/features/dashboard/utils/panel.test.ts | 6 ++++++ public/app/features/dashboard/utils/panel.ts | 12 +++++++++--- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/public/app/features/dashboard/utils/panel.test.ts b/public/app/features/dashboard/utils/panel.test.ts index eeeb664f874..fdb4f16fa17 100644 --- a/public/app/features/dashboard/utils/panel.test.ts +++ b/public/app/features/dashboard/utils/panel.test.ts @@ -33,6 +33,8 @@ describe('applyPanelTimeOverrides', () => { expect(overrides.timeRange.from.toISOString()).toBe(moment([2019, 1, 11, 12]).toISOString()); expect(overrides.timeRange.to.toISOString()).toBe(fakeCurrentDate.toISOString()); + expect(overrides.timeRange.raw.from).toBe('now-2h'); + expect(overrides.timeRange.raw.to).toBe('now'); }); it('should apply time shift', () => { @@ -48,6 +50,8 @@ describe('applyPanelTimeOverrides', () => { expect(overrides.timeRange.from.toISOString()).toBe(expectedFromDate.toISOString()); expect(overrides.timeRange.to.toISOString()).toBe(expectedToDate.toISOString()); + expect((overrides.timeRange.raw.from as moment.Moment).toISOString()).toEqual(expectedFromDate.toISOString()); + expect((overrides.timeRange.raw.to as moment.Moment).toISOString()).toEqual(expectedToDate.toISOString()); }); it('should apply both relative time and time shift', () => { @@ -64,5 +68,7 @@ describe('applyPanelTimeOverrides', () => { expect(overrides.timeRange.from.toISOString()).toBe(expectedFromDate.toISOString()); expect(overrides.timeRange.to.toISOString()).toBe(expectedToDate.toISOString()); + expect((overrides.timeRange.raw.from as moment.Moment).toISOString()).toEqual(expectedFromDate.toISOString()); + expect((overrides.timeRange.raw.to as moment.Moment).toISOString()).toEqual(expectedToDate.toISOString()); }); }); diff --git a/public/app/features/dashboard/utils/panel.ts b/public/app/features/dashboard/utils/panel.ts index cf00a31c71e..00c960bdfaa 100644 --- a/public/app/features/dashboard/utils/panel.ts +++ b/public/app/features/dashboard/utils/panel.ts @@ -142,10 +142,16 @@ export function applyPanelTimeOverrides(panel: PanelModel, timeRange: TimeRange) const timeShift = '-' + timeShiftInterpolated; newTimeData.timeInfo += ' timeshift ' + timeShift; + const from = dateMath.parseDateMath(timeShift, newTimeData.timeRange.from, false); + const to = dateMath.parseDateMath(timeShift, newTimeData.timeRange.to, true); + newTimeData.timeRange = { - from: dateMath.parseDateMath(timeShift, newTimeData.timeRange.from, false), - to: dateMath.parseDateMath(timeShift, newTimeData.timeRange.to, true), - raw: newTimeData.timeRange.raw, + from, + to, + raw: { + from, + to, + }, }; } From 9c54da8f5d2e3c1cc23865af8aafd256e8e65c54 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Sat, 12 Jan 2019 20:43:15 +0100 Subject: [PATCH 14/26] Simplified folder structure in grafana-ui lib --- .../src/{forms => components}/GfFormLabel/GfFormLabel.tsx | 0 .../src/{visualizations => components}/Graph/Graph.tsx | 0 packages/grafana-ui/src/components/index.ts | 2 ++ packages/grafana-ui/src/forms/index.ts | 1 - packages/grafana-ui/src/index.ts | 2 -- packages/grafana-ui/src/visualizations/index.ts | 1 - 6 files changed, 2 insertions(+), 4 deletions(-) rename packages/grafana-ui/src/{forms => components}/GfFormLabel/GfFormLabel.tsx (100%) rename packages/grafana-ui/src/{visualizations => components}/Graph/Graph.tsx (100%) delete mode 100644 packages/grafana-ui/src/forms/index.ts delete mode 100644 packages/grafana-ui/src/visualizations/index.ts diff --git a/packages/grafana-ui/src/forms/GfFormLabel/GfFormLabel.tsx b/packages/grafana-ui/src/components/GfFormLabel/GfFormLabel.tsx similarity index 100% rename from packages/grafana-ui/src/forms/GfFormLabel/GfFormLabel.tsx rename to packages/grafana-ui/src/components/GfFormLabel/GfFormLabel.tsx diff --git a/packages/grafana-ui/src/visualizations/Graph/Graph.tsx b/packages/grafana-ui/src/components/Graph/Graph.tsx similarity index 100% rename from packages/grafana-ui/src/visualizations/Graph/Graph.tsx rename to packages/grafana-ui/src/components/Graph/Graph.tsx diff --git a/packages/grafana-ui/src/components/index.ts b/packages/grafana-ui/src/components/index.ts index 936e1a1c759..028a71b56c2 100644 --- a/packages/grafana-ui/src/components/index.ts +++ b/packages/grafana-ui/src/components/index.ts @@ -14,3 +14,5 @@ export { ColorPicker } from './ColorPicker/ColorPicker'; export { SeriesColorPickerPopover } from './ColorPicker/SeriesColorPickerPopover'; export { SeriesColorPicker } from './ColorPicker/SeriesColorPicker'; export { ThresholdsEditor } from './ThresholdsEditor/ThresholdsEditor'; +export { GfFormLabel } from './GfFormLabel/GfFormLabel'; +export { Graph } from './Graph/Graph'; diff --git a/packages/grafana-ui/src/forms/index.ts b/packages/grafana-ui/src/forms/index.ts deleted file mode 100644 index bb6998b0025..00000000000 --- a/packages/grafana-ui/src/forms/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { GfFormLabel } from './GfFormLabel/GfFormLabel'; diff --git a/packages/grafana-ui/src/index.ts b/packages/grafana-ui/src/index.ts index b22152497b9..974d976bbef 100644 --- a/packages/grafana-ui/src/index.ts +++ b/packages/grafana-ui/src/index.ts @@ -1,5 +1,3 @@ export * from './components'; -export * from './visualizations'; export * from './types'; export * from './utils'; -export * from './forms'; diff --git a/packages/grafana-ui/src/visualizations/index.ts b/packages/grafana-ui/src/visualizations/index.ts deleted file mode 100644 index 967432d37c9..00000000000 --- a/packages/grafana-ui/src/visualizations/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { Graph } from './Graph/Graph'; From 5b59d59afa89f8c4caa9fc5a93c5218883bc3d0c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Sat, 12 Jan 2019 21:43:41 +0100 Subject: [PATCH 15/26] panel option section moved to grafana-ui and new panel option grid component --- .../PanelOptionGrid/PanelOptionGrid.tsx | 15 +++++++++++ .../PanelOptionGrid/_PanelOptionGrid.scss | 11 ++++++++ .../PanelOptionSection.tsx | 0 .../_PanelOptionSection.scss | 27 +++++++++++++++++++ .../ThresholdsEditor/ThresholdsEditor.tsx | 6 ++--- packages/grafana-ui/src/components/index.scss | 2 ++ packages/grafana-ui/src/components/index.ts | 2 ++ .../dashboard/dashgrid/EditorTabBody.tsx | 3 +-- .../dashboard/dashgrid/QueriesTab.tsx | 2 +- .../dashboard/dashgrid/VisualizationTab.tsx | 5 ++-- .../panel/gauge/GaugeOptionsEditor.tsx | 7 +++-- .../plugins/panel/gauge/GaugePanelOptions.tsx | 17 +++++++----- .../app/plugins/panel/gauge/ValueMappings.tsx | 7 +++-- .../app/plugins/panel/gauge/ValueOptions.tsx | 7 +++-- public/app/plugins/panel/gauge/types.ts | 2 ++ public/sass/components/_panel_editor.scss | 27 ------------------- 16 files changed, 86 insertions(+), 54 deletions(-) create mode 100644 packages/grafana-ui/src/components/PanelOptionGrid/PanelOptionGrid.tsx create mode 100644 packages/grafana-ui/src/components/PanelOptionGrid/_PanelOptionGrid.scss rename {public/app/features/dashboard/dashgrid => packages/grafana-ui/src/components/PanelOptionSection}/PanelOptionSection.tsx (100%) create mode 100644 packages/grafana-ui/src/components/PanelOptionSection/_PanelOptionSection.scss create mode 100644 public/app/plugins/panel/gauge/types.ts diff --git a/packages/grafana-ui/src/components/PanelOptionGrid/PanelOptionGrid.tsx b/packages/grafana-ui/src/components/PanelOptionGrid/PanelOptionGrid.tsx new file mode 100644 index 00000000000..48c0d369857 --- /dev/null +++ b/packages/grafana-ui/src/components/PanelOptionGrid/PanelOptionGrid.tsx @@ -0,0 +1,15 @@ +import React, { SFC } from 'react'; + +interface Props { + cols?: number; + children: JSX.Element[] | JSX.Element; +} + +export const PanelOptionGrid: SFC = ({ children }) => { + + return ( +
+ {children} +
+ ); +}; diff --git a/packages/grafana-ui/src/components/PanelOptionGrid/_PanelOptionGrid.scss b/packages/grafana-ui/src/components/PanelOptionGrid/_PanelOptionGrid.scss new file mode 100644 index 00000000000..d26cf82b2b4 --- /dev/null +++ b/packages/grafana-ui/src/components/PanelOptionGrid/_PanelOptionGrid.scss @@ -0,0 +1,11 @@ +.panel-option-grid { + display: grid; + grid-template-columns: repeat(2, 1fr); + grid-row-gap: 10px; + grid-column-gap: 10px; + margin-bottom: 10px; + + @include media-breakpoint-up(md) { + grid-template-columns: repeat(3, 1fr); + } +} diff --git a/public/app/features/dashboard/dashgrid/PanelOptionSection.tsx b/packages/grafana-ui/src/components/PanelOptionSection/PanelOptionSection.tsx similarity index 100% rename from public/app/features/dashboard/dashgrid/PanelOptionSection.tsx rename to packages/grafana-ui/src/components/PanelOptionSection/PanelOptionSection.tsx diff --git a/packages/grafana-ui/src/components/PanelOptionSection/_PanelOptionSection.scss b/packages/grafana-ui/src/components/PanelOptionSection/_PanelOptionSection.scss new file mode 100644 index 00000000000..d95d3f47984 --- /dev/null +++ b/packages/grafana-ui/src/components/PanelOptionSection/_PanelOptionSection.scss @@ -0,0 +1,27 @@ +.panel-option-section { + margin-bottom: 10px; + border: $panel-option-section-border; + border-radius: $border-radius; + background: $page-bg; +} + +.panel-option-section__header { + padding: 4px 20px; + font-size: 1.1rem; + background: $panel-option-section-header-bg; + position: relative; + + .btn { + position: absolute; + right: 0; + top: 0px; + } +} + +.panel-option-section__body { + padding: 20px; + + &--queries { + min-height: 200px; + } +} diff --git a/packages/grafana-ui/src/components/ThresholdsEditor/ThresholdsEditor.tsx b/packages/grafana-ui/src/components/ThresholdsEditor/ThresholdsEditor.tsx index 54165dfadb5..869701677bd 100644 --- a/packages/grafana-ui/src/components/ThresholdsEditor/ThresholdsEditor.tsx +++ b/packages/grafana-ui/src/components/ThresholdsEditor/ThresholdsEditor.tsx @@ -3,6 +3,7 @@ import tinycolor, { ColorInput } from 'tinycolor2'; import { Threshold, BasicGaugeColor } from '../../types'; import { ColorPicker } from '../ColorPicker/ColorPicker'; +import { PanelOptionSection } from '../PanelOptionSection/PanelOptionSection'; export interface Props { thresholds: Threshold[]; @@ -204,8 +205,7 @@ export class ThresholdsEditor extends PureComponent { render() { return ( -
-
Thresholds
+
{this.renderIndicator()} @@ -216,7 +216,7 @@ export class ThresholdsEditor extends PureComponent { {this.renderBase()}
-
+ ); } } diff --git a/packages/grafana-ui/src/components/index.scss b/packages/grafana-ui/src/components/index.scss index c2fe032d24e..9b92aafedee 100644 --- a/packages/grafana-ui/src/components/index.scss +++ b/packages/grafana-ui/src/components/index.scss @@ -3,3 +3,5 @@ @import 'ThresholdsEditor/ThresholdsEditor'; @import 'Tooltip/Tooltip'; @import 'Select/Select'; +@import 'PanelOptionSection/PanelOptionSection'; +@import 'PanelOptionGrid/PanelOptionGrid'; diff --git a/packages/grafana-ui/src/components/index.ts b/packages/grafana-ui/src/components/index.ts index 028a71b56c2..085a3742eb6 100644 --- a/packages/grafana-ui/src/components/index.ts +++ b/packages/grafana-ui/src/components/index.ts @@ -16,3 +16,5 @@ export { SeriesColorPicker } from './ColorPicker/SeriesColorPicker'; export { ThresholdsEditor } from './ThresholdsEditor/ThresholdsEditor'; export { GfFormLabel } from './GfFormLabel/GfFormLabel'; export { Graph } from './Graph/Graph'; +export { PanelOptionSection } from './PanelOptionSection/PanelOptionSection'; +export { PanelOptionGrid } from './PanelOptionGrid/PanelOptionGrid'; diff --git a/public/app/features/dashboard/dashgrid/EditorTabBody.tsx b/public/app/features/dashboard/dashgrid/EditorTabBody.tsx index e86baf0a80b..b3fe3d4f40a 100644 --- a/public/app/features/dashboard/dashgrid/EditorTabBody.tsx +++ b/public/app/features/dashboard/dashgrid/EditorTabBody.tsx @@ -2,9 +2,8 @@ import React, { PureComponent } from 'react'; // Components -import { CustomScrollbar } from '@grafana/ui'; +import { CustomScrollbar, PanelOptionSection } from '@grafana/ui'; import { FadeIn } from 'app/core/components/Animations/FadeIn'; -import { PanelOptionSection } from './PanelOptionSection'; interface Props { children: JSX.Element; diff --git a/public/app/features/dashboard/dashgrid/QueriesTab.tsx b/public/app/features/dashboard/dashgrid/QueriesTab.tsx index a20f8627fba..4e581089629 100644 --- a/public/app/features/dashboard/dashgrid/QueriesTab.tsx +++ b/public/app/features/dashboard/dashgrid/QueriesTab.tsx @@ -9,7 +9,7 @@ import { DataSourcePicker } from 'app/core/components/Select/DataSourcePicker'; import { QueryInspector } from './QueryInspector'; import { QueryOptions } from './QueryOptions'; import { AngularQueryComponentScope } from 'app/features/panel/metrics_tab'; -import { PanelOptionSection } from './PanelOptionSection'; +import { PanelOptionSection } from '@grafana/ui'; // Services import { getDatasourceSrv } from 'app/features/plugins/datasource_srv'; diff --git a/public/app/features/dashboard/dashgrid/VisualizationTab.tsx b/public/app/features/dashboard/dashgrid/VisualizationTab.tsx index bc7102f35dd..b43a0aa406c 100644 --- a/public/app/features/dashboard/dashgrid/VisualizationTab.tsx +++ b/public/app/features/dashboard/dashgrid/VisualizationTab.tsx @@ -9,7 +9,6 @@ import { EditorTabBody, EditorToolbarView } from './EditorTabBody'; import { VizTypePicker } from './VizTypePicker'; import { PluginHelp } from 'app/core/components/PluginHelp/PluginHelp'; import { FadeIn } from 'app/core/components/Animations/FadeIn'; -import { PanelOptionSection } from './PanelOptionSection'; // Types import { PanelModel } from '../panel_model'; @@ -62,13 +61,13 @@ export class VisualizationTab extends PureComponent { } return ( - + <> {PanelOptions ? ( ) : (

Visualization has no options

)} -
+ ); } diff --git a/public/app/plugins/panel/gauge/GaugeOptionsEditor.tsx b/public/app/plugins/panel/gauge/GaugeOptionsEditor.tsx index cb436180b49..39b24687197 100644 --- a/public/app/plugins/panel/gauge/GaugeOptionsEditor.tsx +++ b/public/app/plugins/panel/gauge/GaugeOptionsEditor.tsx @@ -1,5 +1,5 @@ import React, { PureComponent } from 'react'; -import { GaugeOptions, PanelOptionsProps } from '@grafana/ui'; +import { GaugeOptions, PanelOptionsProps, PanelOptionSection } from '@grafana/ui'; import { Switch } from 'app/core/components/Switch/Switch'; import { Label } from '../../../core/components/Label/Label'; @@ -20,8 +20,7 @@ export default class GaugeOptionsEditor extends PureComponent -
Gauge
+
@@ -42,7 +41,7 @@ export default class GaugeOptionsEditor extends PureComponent -
+
); } } diff --git a/public/app/plugins/panel/gauge/GaugePanelOptions.tsx b/public/app/plugins/panel/gauge/GaugePanelOptions.tsx index e43abad61a3..d9fba1411c3 100644 --- a/public/app/plugins/panel/gauge/GaugePanelOptions.tsx +++ b/public/app/plugins/panel/gauge/GaugePanelOptions.tsx @@ -1,5 +1,12 @@ import React, { PureComponent } from 'react'; -import { BasicGaugeColor, GaugeOptions, PanelOptionsProps, ThresholdsEditor, Threshold } from '@grafana/ui'; +import { + BasicGaugeColor, + GaugeOptions, + PanelOptionsProps, + ThresholdsEditor, + Threshold, + PanelOptionGrid, +} from '@grafana/ui'; import ValueOptions from 'app/plugins/panel/gauge/ValueOptions'; import ValueMappings from 'app/plugins/panel/gauge/ValueMappings'; @@ -31,15 +38,13 @@ export default class GaugePanelOptions extends PureComponent -
+ -
+ -
- -
+ ); } diff --git a/public/app/plugins/panel/gauge/ValueMappings.tsx b/public/app/plugins/panel/gauge/ValueMappings.tsx index 4ce0d37b53c..f63435480d6 100644 --- a/public/app/plugins/panel/gauge/ValueMappings.tsx +++ b/public/app/plugins/panel/gauge/ValueMappings.tsx @@ -1,5 +1,5 @@ import React, { PureComponent } from 'react'; -import { GaugeOptions, PanelOptionsProps, MappingType, RangeMap, ValueMap } from '@grafana/ui'; +import { GaugeOptions, PanelOptionsProps, MappingType, RangeMap, ValueMap, PanelOptionSection } from '@grafana/ui'; import MappingRow from './MappingRow'; @@ -75,8 +75,7 @@ export default class ValueMappings extends PureComponent -
Value mappings
+
{mappings.length > 0 && mappings.map((mapping, index) => ( @@ -94,7 +93,7 @@ export default class ValueMappings extends PureComponent
Add mapping
- +
); } } diff --git a/public/app/plugins/panel/gauge/ValueOptions.tsx b/public/app/plugins/panel/gauge/ValueOptions.tsx index 0b30da35d36..5a8ffc5cd09 100644 --- a/public/app/plugins/panel/gauge/ValueOptions.tsx +++ b/public/app/plugins/panel/gauge/ValueOptions.tsx @@ -1,5 +1,5 @@ import React, { PureComponent } from 'react'; -import { GaugeOptions, PanelOptionsProps } from '@grafana/ui'; +import { GaugeOptions, PanelOptionsProps, PanelOptionSection } from '@grafana/ui'; import { Label } from 'app/core/components/Label/Label'; import { Select} from '@grafana/ui'; @@ -40,8 +40,7 @@ export default class ValueOptions extends PureComponent -
Value
+
- +
); } } diff --git a/public/app/plugins/panel/gauge/types.ts b/public/app/plugins/panel/gauge/types.ts new file mode 100644 index 00000000000..139597f9cb0 --- /dev/null +++ b/public/app/plugins/panel/gauge/types.ts @@ -0,0 +1,2 @@ + + diff --git a/public/sass/components/_panel_editor.scss b/public/sass/components/_panel_editor.scss index bfc1c4bb9b5..b2ab91ccb19 100644 --- a/public/sass/components/_panel_editor.scss +++ b/public/sass/components/_panel_editor.scss @@ -230,30 +230,3 @@ min-width: 200px; } -.panel-option-section { - margin-bottom: 10px; - border: $panel-option-section-border; - border-radius: $border-radius; -} - -.panel-option-section__header { - padding: 4px 20px; - font-size: 1.1rem; - background: $panel-option-section-header-bg; - position: relative; - - .btn { - position: absolute; - right: 0; - top: 0px; - } -} - -.panel-option-section__body { - padding: 20px; - background: $page-bg; - - &--queries { - min-height: 200px; - } -} From c11ec79056f0c11bb9f235b5c9505cd013f23373 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Sun, 13 Jan 2019 12:42:21 +0100 Subject: [PATCH 16/26] Minor renames and other fixes --- .../PanelOptionGrid/_PanelOptionGrid.scss | 11 ----------- .../PanelOptionsGrid.tsx} | 4 ++-- .../PanelOptionsGrid/_PanelOptionsGrid.scss | 10 ++++++++++ .../PanelOptionsGroup.tsx} | 8 ++++---- .../_PanelOptionsGroup.scss} | 10 +++++----- .../ThresholdsEditor/ThresholdsEditor.tsx | 6 +++--- packages/grafana-ui/src/components/index.scss | 4 ++-- packages/grafana-ui/src/components/index.ts | 4 ++-- .../features/alerting/partials/alert_tab.html | 10 +++++----- .../dashboard/dashgrid/EditorTabBody.tsx | 6 +++--- .../features/dashboard/dashgrid/QueriesTab.tsx | 10 +++++----- .../dashboard/dashgrid/VisualizationTab.tsx | 6 +++--- .../app/features/panel/partials/general_tab.html | 16 ++++++++-------- .../plugins/panel/gauge/GaugeOptionsEditor.tsx | 6 +++--- .../plugins/panel/gauge/GaugePanelOptions.tsx | 6 +++--- public/app/plugins/panel/gauge/ValueMappings.tsx | 6 +++--- public/app/plugins/panel/gauge/ValueOptions.tsx | 6 +++--- public/sass/_variables.dark.scss | 4 ++-- public/sass/_variables.light.scss | 4 ++-- 19 files changed, 68 insertions(+), 69 deletions(-) delete mode 100644 packages/grafana-ui/src/components/PanelOptionGrid/_PanelOptionGrid.scss rename packages/grafana-ui/src/components/{PanelOptionGrid/PanelOptionGrid.tsx => PanelOptionsGrid/PanelOptionsGrid.tsx} (60%) create mode 100644 packages/grafana-ui/src/components/PanelOptionsGrid/_PanelOptionsGrid.scss rename packages/grafana-ui/src/components/{PanelOptionSection/PanelOptionSection.tsx => PanelOptionsGroup/PanelOptionsGroup.tsx} (65%) rename packages/grafana-ui/src/components/{PanelOptionSection/_PanelOptionSection.scss => PanelOptionsGroup/_PanelOptionsGroup.scss} (61%) diff --git a/packages/grafana-ui/src/components/PanelOptionGrid/_PanelOptionGrid.scss b/packages/grafana-ui/src/components/PanelOptionGrid/_PanelOptionGrid.scss deleted file mode 100644 index d26cf82b2b4..00000000000 --- a/packages/grafana-ui/src/components/PanelOptionGrid/_PanelOptionGrid.scss +++ /dev/null @@ -1,11 +0,0 @@ -.panel-option-grid { - display: grid; - grid-template-columns: repeat(2, 1fr); - grid-row-gap: 10px; - grid-column-gap: 10px; - margin-bottom: 10px; - - @include media-breakpoint-up(md) { - grid-template-columns: repeat(3, 1fr); - } -} diff --git a/packages/grafana-ui/src/components/PanelOptionGrid/PanelOptionGrid.tsx b/packages/grafana-ui/src/components/PanelOptionsGrid/PanelOptionsGrid.tsx similarity index 60% rename from packages/grafana-ui/src/components/PanelOptionGrid/PanelOptionGrid.tsx rename to packages/grafana-ui/src/components/PanelOptionsGrid/PanelOptionsGrid.tsx index 48c0d369857..0636ec4a9da 100644 --- a/packages/grafana-ui/src/components/PanelOptionGrid/PanelOptionGrid.tsx +++ b/packages/grafana-ui/src/components/PanelOptionsGrid/PanelOptionsGrid.tsx @@ -5,10 +5,10 @@ interface Props { children: JSX.Element[] | JSX.Element; } -export const PanelOptionGrid: SFC = ({ children }) => { +export const PanelOptionsGrid: SFC = ({ children }) => { return ( -
+
{children}
); diff --git a/packages/grafana-ui/src/components/PanelOptionsGrid/_PanelOptionsGrid.scss b/packages/grafana-ui/src/components/PanelOptionsGrid/_PanelOptionsGrid.scss new file mode 100644 index 00000000000..1cd26867a97 --- /dev/null +++ b/packages/grafana-ui/src/components/PanelOptionsGrid/_PanelOptionsGrid.scss @@ -0,0 +1,10 @@ +.panel-options-grid { + display: grid; + grid-template-columns: repeat(1, 1fr); + grid-row-gap: 10px; + grid-column-gap: 10px; + + @include media-breakpoint-up(lg) { + grid-template-columns: repeat(3, 1fr); + } +} diff --git a/packages/grafana-ui/src/components/PanelOptionSection/PanelOptionSection.tsx b/packages/grafana-ui/src/components/PanelOptionsGroup/PanelOptionsGroup.tsx similarity index 65% rename from packages/grafana-ui/src/components/PanelOptionSection/PanelOptionSection.tsx rename to packages/grafana-ui/src/components/PanelOptionsGroup/PanelOptionsGroup.tsx index f38d99d2237..7ce4b8335ff 100644 --- a/packages/grafana-ui/src/components/PanelOptionSection/PanelOptionSection.tsx +++ b/packages/grafana-ui/src/components/PanelOptionsGroup/PanelOptionsGroup.tsx @@ -7,11 +7,11 @@ interface Props { children: JSX.Element | JSX.Element[]; } -export const PanelOptionSection: SFC = props => { +export const PanelOptionsGroup: SFC = props => { return ( -
+
{props.title && ( -
+
{props.title} {props.onClose && (
)} -
{props.children}
+
{props.children}
); }; diff --git a/packages/grafana-ui/src/components/PanelOptionSection/_PanelOptionSection.scss b/packages/grafana-ui/src/components/PanelOptionsGroup/_PanelOptionsGroup.scss similarity index 61% rename from packages/grafana-ui/src/components/PanelOptionSection/_PanelOptionSection.scss rename to packages/grafana-ui/src/components/PanelOptionsGroup/_PanelOptionsGroup.scss index d95d3f47984..9f5d4f02695 100644 --- a/packages/grafana-ui/src/components/PanelOptionSection/_PanelOptionSection.scss +++ b/packages/grafana-ui/src/components/PanelOptionsGroup/_PanelOptionsGroup.scss @@ -1,14 +1,14 @@ -.panel-option-section { +.panel-options-group { margin-bottom: 10px; - border: $panel-option-section-border; + border: $panel-options-group-border; border-radius: $border-radius; background: $page-bg; } -.panel-option-section__header { +.panel-options-group__header { padding: 4px 20px; font-size: 1.1rem; - background: $panel-option-section-header-bg; + background: $panel-options-group-header-bg; position: relative; .btn { @@ -18,7 +18,7 @@ } } -.panel-option-section__body { +.panel-options-group__body { padding: 20px; &--queries { diff --git a/packages/grafana-ui/src/components/ThresholdsEditor/ThresholdsEditor.tsx b/packages/grafana-ui/src/components/ThresholdsEditor/ThresholdsEditor.tsx index 869701677bd..c635b9cb4f5 100644 --- a/packages/grafana-ui/src/components/ThresholdsEditor/ThresholdsEditor.tsx +++ b/packages/grafana-ui/src/components/ThresholdsEditor/ThresholdsEditor.tsx @@ -3,7 +3,7 @@ import tinycolor, { ColorInput } from 'tinycolor2'; import { Threshold, BasicGaugeColor } from '../../types'; import { ColorPicker } from '../ColorPicker/ColorPicker'; -import { PanelOptionSection } from '../PanelOptionSection/PanelOptionSection'; +import { PanelOptionsGroup } from '../PanelOptionsGroup/PanelOptionsGroup'; export interface Props { thresholds: Threshold[]; @@ -205,7 +205,7 @@ export class ThresholdsEditor extends PureComponent { render() { return ( - +
{this.renderIndicator()} @@ -216,7 +216,7 @@ export class ThresholdsEditor extends PureComponent { {this.renderBase()}
-
+ ); } } diff --git a/packages/grafana-ui/src/components/index.scss b/packages/grafana-ui/src/components/index.scss index 9b92aafedee..5a9263844a4 100644 --- a/packages/grafana-ui/src/components/index.scss +++ b/packages/grafana-ui/src/components/index.scss @@ -3,5 +3,5 @@ @import 'ThresholdsEditor/ThresholdsEditor'; @import 'Tooltip/Tooltip'; @import 'Select/Select'; -@import 'PanelOptionSection/PanelOptionSection'; -@import 'PanelOptionGrid/PanelOptionGrid'; +@import 'PanelOptionsGroup/PanelOptionsGroup'; +@import 'PanelOptionsGrid/PanelOptionsGrid'; diff --git a/packages/grafana-ui/src/components/index.ts b/packages/grafana-ui/src/components/index.ts index 085a3742eb6..5420fcf14b7 100644 --- a/packages/grafana-ui/src/components/index.ts +++ b/packages/grafana-ui/src/components/index.ts @@ -16,5 +16,5 @@ export { SeriesColorPicker } from './ColorPicker/SeriesColorPicker'; export { ThresholdsEditor } from './ThresholdsEditor/ThresholdsEditor'; export { GfFormLabel } from './GfFormLabel/GfFormLabel'; export { Graph } from './Graph/Graph'; -export { PanelOptionSection } from './PanelOptionSection/PanelOptionSection'; -export { PanelOptionGrid } from './PanelOptionGrid/PanelOptionGrid'; +export { PanelOptionsGroup } from './PanelOptionsGroup/PanelOptionsGroup'; +export { PanelOptionsGrid } from './PanelOptionsGrid/PanelOptionsGrid'; diff --git a/public/app/features/alerting/partials/alert_tab.html b/public/app/features/alerting/partials/alert_tab.html index 90e0c7bbac2..9dfd3da47f9 100644 --- a/public/app/features/alerting/partials/alert_tab.html +++ b/public/app/features/alerting/partials/alert_tab.html @@ -2,8 +2,8 @@
{{ctrl.error}}
-
-
+
+

Rule

@@ -125,9 +125,9 @@
-
-
Notifications
-
+
+
Notifications
+
Send to diff --git a/public/app/features/dashboard/dashgrid/EditorTabBody.tsx b/public/app/features/dashboard/dashgrid/EditorTabBody.tsx index b3fe3d4f40a..dbea7ed59bc 100644 --- a/public/app/features/dashboard/dashgrid/EditorTabBody.tsx +++ b/public/app/features/dashboard/dashgrid/EditorTabBody.tsx @@ -2,7 +2,7 @@ import React, { PureComponent } from 'react'; // Components -import { CustomScrollbar, PanelOptionSection } from '@grafana/ui'; +import { CustomScrollbar, PanelOptionsGroup } from '@grafana/ui'; import { FadeIn } from 'app/core/components/Animations/FadeIn'; interface Props { @@ -96,9 +96,9 @@ export class EditorTabBody extends PureComponent { renderOpenView(view: EditorToolbarView) { return ( - + {view.render()} - + ); } diff --git a/public/app/features/dashboard/dashgrid/QueriesTab.tsx b/public/app/features/dashboard/dashgrid/QueriesTab.tsx index 4e581089629..47c4f358136 100644 --- a/public/app/features/dashboard/dashgrid/QueriesTab.tsx +++ b/public/app/features/dashboard/dashgrid/QueriesTab.tsx @@ -9,7 +9,7 @@ import { DataSourcePicker } from 'app/core/components/Select/DataSourcePicker'; import { QueryInspector } from './QueryInspector'; import { QueryOptions } from './QueryOptions'; import { AngularQueryComponentScope } from 'app/features/panel/metrics_tab'; -import { PanelOptionSection } from '@grafana/ui'; +import { PanelOptionsGroup } from '@grafana/ui'; // Services import { getDatasourceSrv } from 'app/features/plugins/datasource_srv'; @@ -216,7 +216,7 @@ export class QueriesTab extends PureComponent { return ( <> - +
(this.element = element)} /> @@ -239,10 +239,10 @@ export class QueriesTab extends PureComponent {
- - + + - + ); diff --git a/public/app/features/dashboard/dashgrid/VisualizationTab.tsx b/public/app/features/dashboard/dashgrid/VisualizationTab.tsx index b43a0aa406c..ad569a9ff90 100644 --- a/public/app/features/dashboard/dashgrid/VisualizationTab.tsx +++ b/public/app/features/dashboard/dashgrid/VisualizationTab.tsx @@ -111,9 +111,9 @@ export class VisualizationTab extends PureComponent { for (let i = 0; i < panelCtrl.editorTabs.length; i++) { template += ` -
` + - (i > 0 ? `
{{ctrl.editorTabs[${i}].title}}
` : '') + - `
+
` + + (i > 0 ? `
{{ctrl.editorTabs[${i}].title}}
` : '') + + `
diff --git a/public/app/features/panel/partials/general_tab.html b/public/app/features/panel/partials/general_tab.html index 8881d2c28a4..ceae445f3ed 100644 --- a/public/app/features/panel/partials/general_tab.html +++ b/public/app/features/panel/partials/general_tab.html @@ -1,6 +1,6 @@ -
+
-
+
Title @@ -17,9 +17,9 @@
-
-
Repeating
-
+
+
Repeating
+
Repeat @@ -46,9 +46,9 @@
-
-
Drilldown Links
-
+
+
Drilldown Links
+
diff --git a/public/app/plugins/panel/gauge/GaugeOptionsEditor.tsx b/public/app/plugins/panel/gauge/GaugeOptionsEditor.tsx index 39b24687197..f1f78ab1172 100644 --- a/public/app/plugins/panel/gauge/GaugeOptionsEditor.tsx +++ b/public/app/plugins/panel/gauge/GaugeOptionsEditor.tsx @@ -1,5 +1,5 @@ import React, { PureComponent } from 'react'; -import { GaugeOptions, PanelOptionsProps, PanelOptionSection } from '@grafana/ui'; +import { GaugeOptions, PanelOptionsProps, PanelOptionsGroup } from '@grafana/ui'; import { Switch } from 'app/core/components/Switch/Switch'; import { Label } from '../../../core/components/Label/Label'; @@ -20,7 +20,7 @@ export default class GaugeOptionsEditor extends PureComponent +
@@ -41,7 +41,7 @@ export default class GaugeOptionsEditor extends PureComponent - + ); } } diff --git a/public/app/plugins/panel/gauge/GaugePanelOptions.tsx b/public/app/plugins/panel/gauge/GaugePanelOptions.tsx index d9fba1411c3..a5334b0c6e1 100644 --- a/public/app/plugins/panel/gauge/GaugePanelOptions.tsx +++ b/public/app/plugins/panel/gauge/GaugePanelOptions.tsx @@ -5,7 +5,7 @@ import { PanelOptionsProps, ThresholdsEditor, Threshold, - PanelOptionGrid, + PanelOptionsGrid, } from '@grafana/ui'; import ValueOptions from 'app/plugins/panel/gauge/ValueOptions'; @@ -38,11 +38,11 @@ export default class GaugePanelOptions extends PureComponent - + - + diff --git a/public/app/plugins/panel/gauge/ValueMappings.tsx b/public/app/plugins/panel/gauge/ValueMappings.tsx index f63435480d6..9a3f87450f4 100644 --- a/public/app/plugins/panel/gauge/ValueMappings.tsx +++ b/public/app/plugins/panel/gauge/ValueMappings.tsx @@ -1,5 +1,5 @@ import React, { PureComponent } from 'react'; -import { GaugeOptions, PanelOptionsProps, MappingType, RangeMap, ValueMap, PanelOptionSection } from '@grafana/ui'; +import { GaugeOptions, PanelOptionsProps, MappingType, RangeMap, ValueMap, PanelOptionsGroup } from '@grafana/ui'; import MappingRow from './MappingRow'; @@ -75,7 +75,7 @@ export default class ValueMappings extends PureComponent +
{mappings.length > 0 && mappings.map((mapping, index) => ( @@ -93,7 +93,7 @@ export default class ValueMappings extends PureComponent
Add mapping
- +
); } } diff --git a/public/app/plugins/panel/gauge/ValueOptions.tsx b/public/app/plugins/panel/gauge/ValueOptions.tsx index 5a8ffc5cd09..7cfbb382f7b 100644 --- a/public/app/plugins/panel/gauge/ValueOptions.tsx +++ b/public/app/plugins/panel/gauge/ValueOptions.tsx @@ -1,5 +1,5 @@ import React, { PureComponent } from 'react'; -import { GaugeOptions, PanelOptionsProps, PanelOptionSection } from '@grafana/ui'; +import { GaugeOptions, PanelOptionsProps, PanelOptionsGroup } from '@grafana/ui'; import { Label } from 'app/core/components/Label/Label'; import { Select} from '@grafana/ui'; @@ -40,7 +40,7 @@ export default class ValueOptions extends PureComponent +
- +
); } } diff --git a/public/sass/_variables.dark.scss b/public/sass/_variables.dark.scss index 5640ff1775e..da6328b3c11 100644 --- a/public/sass/_variables.dark.scss +++ b/public/sass/_variables.dark.scss @@ -391,8 +391,8 @@ $panel-editor-tabs-line-color: #e3e3e3; $panel-editor-viz-item-bg-hover: darken($blue, 47%); $panel-editor-viz-item-bg-hover-active: darken($orange, 45%); -$panel-option-section-border: 1px solid $dark-3; -$panel-option-section-header-bg: linear-gradient(0deg, $gray-blue, $dark-1); +$panel-options-group-border: 1px solid $dark-3; +$panel-options-group-header-bg: linear-gradient(0deg, $gray-blue, $dark-1); $panel-grid-placeholder-bg: darken($blue, 47%); $panel-grid-placeholder-shadow: 0 0 4px $blue; diff --git a/public/sass/_variables.light.scss b/public/sass/_variables.light.scss index be8df389c1b..cca183233be 100644 --- a/public/sass/_variables.light.scss +++ b/public/sass/_variables.light.scss @@ -399,8 +399,8 @@ $panel-editor-tabs-line-color: $dark-5; $panel-editor-viz-item-bg-hover: lighten($blue, 62%); $panel-editor-viz-item-bg-hover-active: lighten($orange, 34%); -$panel-option-section-border: 1px solid $gray-6; -$panel-option-section-header-bg: linear-gradient(0deg, $gray-6, $gray-7); +$panel-options-group-border: 1px solid $gray-6; +$panel-options-group-header-bg: linear-gradient(0deg, $gray-6, $gray-7); $panel-grid-placeholder-bg: lighten($blue, 62%); $panel-grid-placeholder-shadow: 0 0 4px $blue-light; From 9e0f961fd5823f844bec81e3d4a4373acf3bbd2f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Sun, 13 Jan 2019 12:51:21 +0100 Subject: [PATCH 17/26] updated snapshot --- .../gauge/__snapshots__/ValueMappings.test.tsx.snap | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/public/app/plugins/panel/gauge/__snapshots__/ValueMappings.test.tsx.snap b/public/app/plugins/panel/gauge/__snapshots__/ValueMappings.test.tsx.snap index 8a05cb7e91b..592b3326421 100644 --- a/public/app/plugins/panel/gauge/__snapshots__/ValueMappings.test.tsx.snap +++ b/public/app/plugins/panel/gauge/__snapshots__/ValueMappings.test.tsx.snap @@ -1,14 +1,9 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`Render should render component 1`] = ` -
-
- Value mappings -
-
+ `; From 8def73ba13e5c7ad9b00d385873ee9352aff3cfd Mon Sep 17 00:00:00 2001 From: Sofia Papagiannaki Date: Sun, 13 Jan 2019 21:30:20 +0200 Subject: [PATCH 18/26] Fix Error 500 on unexisting /api/alert-notification/ --- pkg/api/alerting.go | 4 ++++ pkg/api/alerting_test.go | 6 ++++++ 2 files changed, 10 insertions(+) diff --git a/pkg/api/alerting.go b/pkg/api/alerting.go index 66b3b504946..19fb4efd7e8 100644 --- a/pkg/api/alerting.go +++ b/pkg/api/alerting.go @@ -212,6 +212,10 @@ func GetAlertNotificationByID(c *m.ReqContext) Response { return Error(500, "Failed to get alert notifications", err) } + if query.Result == nil { + return Error(404, "Alert notification not found", nil) + } + return JSON(200, dtos.NewAlertNotification(query.Result)) } diff --git a/pkg/api/alerting_test.go b/pkg/api/alerting_test.go index 331beeef5e4..168193e377f 100644 --- a/pkg/api/alerting_test.go +++ b/pkg/api/alerting_test.go @@ -119,6 +119,12 @@ func TestAlertingApiEndpoint(t *testing.T) { So(getAlertsQuery.Limit, ShouldEqual, 5) So(getAlertsQuery.Query, ShouldEqual, "alertQuery") }) + + loggedInUserScenarioWithRole("When calling GET on", "GET", "/api/alert-notifications/1", "/alert-notifications/:notificationId", m.ROLE_ADMIN, func(sc *scenarioContext) { + sc.handlerFunc = GetAlertNotificationByID + sc.fakeReqWithParams("GET", sc.url, map[string]string{}).exec() + So(sc.resp.Code, ShouldEqual, 404) + }) }) } From 110ffb69dee696dca305167d0167c4c78462fde8 Mon Sep 17 00:00:00 2001 From: fredbcode Date: Fri, 11 Jan 2019 08:56:50 +0100 Subject: [PATCH 19/26] Fix bug tls renegociation problem in Notification channel (webhook) #14800 --- pkg/services/notifications/webhook.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pkg/services/notifications/webhook.go b/pkg/services/notifications/webhook.go index dbe441c915e..2be3b145372 100644 --- a/pkg/services/notifications/webhook.go +++ b/pkg/services/notifications/webhook.go @@ -3,6 +3,7 @@ package notifications import ( "bytes" "context" + "crypto/tls" "fmt" "io" "io/ioutil" @@ -26,6 +27,9 @@ type Webhook struct { } var netTransport = &http.Transport{ + TLSClientConfig: &tls.Config{ + Renegotiation: tls.RenegotiateFreelyAsClient, + }, Proxy: http.ProxyFromEnvironment, Dial: (&net.Dialer{ Timeout: 30 * time.Second, From 1fea09ba54c8e8ffab349ea337b9cfe58d62c214 Mon Sep 17 00:00:00 2001 From: Daniel Lee Date: Sun, 13 Jan 2019 23:37:53 +0100 Subject: [PATCH 20/26] units: adds back velocity units. Fixes #14851 --- packages/grafana-ui/src/utils/valueFormats/categories.ts | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/packages/grafana-ui/src/utils/valueFormats/categories.ts b/packages/grafana-ui/src/utils/valueFormats/categories.ts index 98739343beb..4be76b13642 100644 --- a/packages/grafana-ui/src/utils/valueFormats/categories.ts +++ b/packages/grafana-ui/src/utils/valueFormats/categories.ts @@ -299,6 +299,15 @@ export const getCategories = (): ValueFormatCategory[] => [ { name: 'writes/min (wpm)', id: 'wpm', fn: simpleCountUnit('wpm') }, ], }, + { + name: 'velocity', + formats: [ + { name: 'metres/second (m/s)', id: 'velocityms', fn: toFixedUnit('m/s') }, + { name: 'kilometers/hour (km/h)', id: 'velocitykmh', fn: toFixedUnit('km/h') }, + { name: 'miles/hour (mph)', id: 'velocitymph', fn: toFixedUnit('mph') }, + { name: 'knot (kn)', id: 'velocityknot', fn: toFixedUnit('kn') }, + ] + }, { name: 'volume', formats: [ From b3b1c21d772929cde9b5f6d5adcc8ee97b5608ec Mon Sep 17 00:00:00 2001 From: Dominik Prokop Date: Mon, 14 Jan 2019 09:45:55 +0100 Subject: [PATCH 21/26] Fixed Gauge being cropped when resizing panel Enabled import of SASS viariables into TS --- public/app/plugins/panel/gauge/GaugePanel.tsx | 10 +++++++++- public/sass/_variables.scss | 9 ++++++++- public/sass/_variables.scss.d.ts | 8 ++++++++ tsconfig.json | 3 ++- 4 files changed, 27 insertions(+), 3 deletions(-) create mode 100644 public/sass/_variables.scss.d.ts diff --git a/public/app/plugins/panel/gauge/GaugePanel.tsx b/public/app/plugins/panel/gauge/GaugePanel.tsx index 79220daf37a..3aedfcf1f67 100644 --- a/public/app/plugins/panel/gauge/GaugePanel.tsx +++ b/public/app/plugins/panel/gauge/GaugePanel.tsx @@ -3,6 +3,7 @@ import { GaugeOptions, PanelProps, NullValueMode } from '@grafana/ui'; import { getTimeSeriesVMs } from 'app/viz/state/timeSeries'; import Gauge from 'app/viz/Gauge'; +import variables from 'sass/_variables.scss'; interface Props extends PanelProps {} @@ -15,6 +16,13 @@ export class GaugePanel extends PureComponent { nullValueMode: NullValueMode.Ignore, }); - return ; + return ( + + ); } } diff --git a/public/sass/_variables.scss b/public/sass/_variables.scss index 111ee870658..a10fefadf10 100644 --- a/public/sass/_variables.scss +++ b/public/sass/_variables.scss @@ -189,7 +189,9 @@ $side-menu-width: 60px; // dashboard $panel-margin: 10px; $dashboard-padding: $panel-margin * 2; -$panel-padding: 0px 10px 5px 10px; +$panel-horizontal-padding: 10; +$panel-vertical-padding: 5; +$panel-padding: 0px $panel-horizontal-padding+0px $panel-vertical-padding+0px $panel-horizontal-padding+0px; // tabs $tabs-padding: 10px 15px 9px; @@ -202,3 +204,8 @@ $external-services: ( oauth: (bgColor: #262628, borderColor: #393939, icon: '') ) !default; + +:export { + panelHorizontalPadding: $panel-horizontal-padding; + panelVerticalPadding: $panel-vertical-padding; +} diff --git a/public/sass/_variables.scss.d.ts b/public/sass/_variables.scss.d.ts new file mode 100644 index 00000000000..f3de2340419 --- /dev/null +++ b/public/sass/_variables.scss.d.ts @@ -0,0 +1,8 @@ +export interface GrafanaVariables { + 'panelHorizontalPadding': number; + 'panelVerticalPadding': number; +} + +declare const variables: GrafanaVariables; + +export default variables; diff --git a/tsconfig.json b/tsconfig.json index 3c8c41f34e2..5772b931820 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -28,7 +28,8 @@ "pretty": true, "typeRoots": ["node_modules/@types", "types"], "paths": { - "app": ["app"] + "app": ["app"], + "sass": ["sass"] } }, "include": ["public/app/**/*.ts", "public/app/**/*.tsx", "public/test/**/*.ts"] From b1f5a232da7b9acb6026fec87f5bba6cbd2180c3 Mon Sep 17 00:00:00 2001 From: Leonard Gram Date: Mon, 14 Jan 2019 13:42:58 +0100 Subject: [PATCH 22/26] build: build specific enterprise version when releasing. --- scripts/build/prepare-enterprise.sh | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/scripts/build/prepare-enterprise.sh b/scripts/build/prepare-enterprise.sh index 0e1c3da2dbd..a2ab269d1cb 100755 --- a/scripts/build/prepare-enterprise.sh +++ b/scripts/build/prepare-enterprise.sh @@ -1,6 +1,15 @@ #!/bin/bash cd .. -git clone -b master --single-branch git@github.com:grafana/grafana-enterprise.git --depth 1 + + +if [ -z "$CIRCLE_TAG" ]; then + _target="master" +else + _target="$CIRCLE_TAG" +fi + +git clone -b "$_target" --single-branch git@github.com:grafana/grafana-enterprise.git --depth 1 + cd grafana-enterprise ./build.sh From 37c5ced009bebace09bdd057dc5a5558f03d7f20 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Mon, 14 Jan 2019 14:03:08 +0100 Subject: [PATCH 23/26] Updated singlestat to use new value format function syntax and capitalized unit categories, fixes #12871 --- .../src/utils/valueFormats/categories.ts | 48 +++++++++---------- public/app/plugins/panel/singlestat/module.ts | 10 +++- 2 files changed, 32 insertions(+), 26 deletions(-) diff --git a/packages/grafana-ui/src/utils/valueFormats/categories.ts b/packages/grafana-ui/src/utils/valueFormats/categories.ts index 4be76b13642..d7410c22276 100644 --- a/packages/grafana-ui/src/utils/valueFormats/categories.ts +++ b/packages/grafana-ui/src/utils/valueFormats/categories.ts @@ -22,7 +22,7 @@ import { binarySIPrefix, currency, decimalSIPrefix } from './symbolFormatters'; export const getCategories = (): ValueFormatCategory[] => [ { - name: 'none', + name: 'Misc', formats: [ { name: 'none', id: 'none', fn: toFixed }, { @@ -41,7 +41,7 @@ export const getCategories = (): ValueFormatCategory[] => [ ], }, { - name: 'acceleration', + name: 'Acceleration', formats: [ { name: 'Meters/sec²', id: 'accMS2', fn: toFixedUnit('m/sec²') }, { name: 'Feet/sec²', id: 'accFS2', fn: toFixedUnit('f/sec²') }, @@ -49,7 +49,7 @@ export const getCategories = (): ValueFormatCategory[] => [ ], }, { - name: 'angle', + name: 'Angle', formats: [ { name: 'Degrees (°)', id: 'degree', fn: toFixedUnit('°') }, { name: 'Radians', id: 'radian', fn: toFixedUnit('rad') }, @@ -57,7 +57,7 @@ export const getCategories = (): ValueFormatCategory[] => [ ], }, { - name: 'area', + name: 'Area', formats: [ { name: 'Square Meters (m²)', id: 'areaM2', fn: toFixedUnit('m²') }, { name: 'Square Feet (ft²)', id: 'areaF2', fn: toFixedUnit('ft²') }, @@ -65,7 +65,7 @@ export const getCategories = (): ValueFormatCategory[] => [ ], }, { - name: 'computation throughput', + name: 'Computation', formats: [ { name: 'FLOP/s', id: 'flops', fn: decimalSIPrefix('FLOP/s') }, { name: 'MFLOP/s', id: 'mflops', fn: decimalSIPrefix('FLOP/s', 2) }, @@ -76,7 +76,7 @@ export const getCategories = (): ValueFormatCategory[] => [ ], }, { - name: 'concentration', + name: 'Concentration', formats: [ { name: 'parts-per-million (ppm)', id: 'ppm', fn: toFixedUnit('ppm') }, { name: 'parts-per-billion (ppb)', id: 'conppb', fn: toFixedUnit('ppb') }, @@ -93,7 +93,7 @@ export const getCategories = (): ValueFormatCategory[] => [ ], }, { - name: 'currency', + name: 'Currency', formats: [ { name: 'Dollars ($)', id: 'currencyUSD', fn: currency('$') }, { name: 'Pounds (£)', id: 'currencyGBP', fn: currency('£') }, @@ -113,7 +113,7 @@ export const getCategories = (): ValueFormatCategory[] => [ ], }, { - name: 'data (IEC)', + name: 'Data (IEC)', formats: [ { name: 'bits', id: 'bits', fn: binarySIPrefix('b') }, { name: 'bytes', id: 'bytes', fn: binarySIPrefix('B') }, @@ -123,7 +123,7 @@ export const getCategories = (): ValueFormatCategory[] => [ ], }, { - name: 'data (Metric)', + name: 'Data (Metric)', formats: [ { name: 'bits', id: 'decbits', fn: decimalSIPrefix('d') }, { name: 'bytes', id: 'decbytes', fn: decimalSIPrefix('B') }, @@ -133,7 +133,7 @@ export const getCategories = (): ValueFormatCategory[] => [ ], }, { - name: 'data rate', + name: 'Data Rate', formats: [ { name: 'packets/sec', id: 'pps', fn: decimalSIPrefix('pps') }, { name: 'bits/sec', id: 'bps', fn: decimalSIPrefix('bps') }, @@ -147,7 +147,7 @@ export const getCategories = (): ValueFormatCategory[] => [ ], }, { - name: 'date & time', + name: 'Date & Time', formats: [ { name: 'YYYY-MM-DD HH:mm:ss', id: 'dateTimeAsIso', fn: dateTimeAsIso }, { name: 'DD/MM/YYYY h:mm:ss a', id: 'dateTimeAsUS', fn: dateTimeAsUS }, @@ -155,7 +155,7 @@ export const getCategories = (): ValueFormatCategory[] => [ ], }, { - name: 'energy', + name: 'Energy', formats: [ { name: 'Watt (W)', id: 'watt', fn: decimalSIPrefix('W') }, { name: 'Kilowatt (kW)', id: 'kwatt', fn: decimalSIPrefix('W', 1) }, @@ -182,7 +182,7 @@ export const getCategories = (): ValueFormatCategory[] => [ ], }, { - name: 'flow', + name: 'Flow', formats: [ { name: 'Gallons/min (gpm)', id: 'flowgpm', fn: toFixedUnit('gpm') }, { name: 'Cubic meters/sec (cms)', id: 'flowcms', fn: toFixedUnit('cms') }, @@ -194,7 +194,7 @@ export const getCategories = (): ValueFormatCategory[] => [ ], }, { - name: 'force', + name: 'Force', formats: [ { name: 'Newton-meters (Nm)', id: 'forceNm', fn: decimalSIPrefix('Nm') }, { name: 'Kilonewton-meters (kNm)', id: 'forcekNm', fn: decimalSIPrefix('Nm', 1) }, @@ -203,7 +203,7 @@ export const getCategories = (): ValueFormatCategory[] => [ ], }, { - name: 'hash rate', + name: 'Hash Rate', formats: [ { name: 'hashes/sec', id: 'Hs', fn: decimalSIPrefix('H/s') }, { name: 'kilohashes/sec', id: 'KHs', fn: decimalSIPrefix('H/s', 1) }, @@ -215,7 +215,7 @@ export const getCategories = (): ValueFormatCategory[] => [ ], }, { - name: 'mass', + name: 'Mass', formats: [ { name: 'milligram (mg)', id: 'massmg', fn: decimalSIPrefix('g', -1) }, { name: 'gram (g)', id: 'massg', fn: decimalSIPrefix('g') }, @@ -234,7 +234,7 @@ export const getCategories = (): ValueFormatCategory[] => [ ], }, { - name: 'pressure', + name: 'Pressure', formats: [ { name: 'Millibars', id: 'pressurembar', fn: decimalSIPrefix('bar', -1) }, { name: 'Bars', id: 'pressurebar', fn: decimalSIPrefix('bar') }, @@ -246,7 +246,7 @@ export const getCategories = (): ValueFormatCategory[] => [ ], }, { - name: 'radiation', + name: 'Radiation', formats: [ { name: 'Becquerel (Bq)', id: 'radbq', fn: decimalSIPrefix('Bq') }, { name: 'curie (Ci)', id: 'radci', fn: decimalSIPrefix('Ci') }, @@ -260,7 +260,7 @@ export const getCategories = (): ValueFormatCategory[] => [ ], }, { - name: 'temperature', + name: 'Temperature', formats: [ { name: 'Celsius (°C)', id: 'celsius', fn: toFixedUnit('°C') }, { name: 'Farenheit (°F)', id: 'farenheit', fn: toFixedUnit('°F') }, @@ -268,7 +268,7 @@ export const getCategories = (): ValueFormatCategory[] => [ ], }, { - name: 'time', + name: 'Time', formats: [ { name: 'Hertz (1/s)', id: 'hertz', fn: decimalSIPrefix('Hz') }, { name: 'nanoseconds (ns)', id: 'ns', fn: toNanoSeconds }, @@ -287,7 +287,7 @@ export const getCategories = (): ValueFormatCategory[] => [ ], }, { - name: 'throughput', + name: 'Throughput', formats: [ { name: 'ops/sec (ops)', id: 'ops', fn: simpleCountUnit('ops') }, { name: 'requests/sec (rps)', id: 'reqps', fn: simpleCountUnit('reqps') }, @@ -300,7 +300,7 @@ export const getCategories = (): ValueFormatCategory[] => [ ], }, { - name: 'velocity', + name: 'Velocity', formats: [ { name: 'metres/second (m/s)', id: 'velocityms', fn: toFixedUnit('m/s') }, { name: 'kilometers/hour (km/h)', id: 'velocitykmh', fn: toFixedUnit('km/h') }, @@ -309,7 +309,7 @@ export const getCategories = (): ValueFormatCategory[] => [ ] }, { - name: 'volume', + name: 'Volume', formats: [ { name: 'millilitre (mL)', id: 'mlitre', fn: decimalSIPrefix('L', -1) }, { name: 'litre (L)', id: 'litre', fn: decimalSIPrefix('L') }, @@ -318,5 +318,5 @@ export const getCategories = (): ValueFormatCategory[] => [ { name: 'cubic decimetre', id: 'dm3', fn: toFixedUnit('dm³') }, { name: 'gallons', id: 'gallons', fn: toFixedUnit('gal') }, ], - }, + } ]; diff --git a/public/app/plugins/panel/singlestat/module.ts b/public/app/plugins/panel/singlestat/module.ts index f83151a0c46..b8e24616f0a 100644 --- a/public/app/plugins/panel/singlestat/module.ts +++ b/public/app/plugins/panel/singlestat/module.ts @@ -312,14 +312,20 @@ class SingleStatCtrl extends MetricsPanelCtrl { const formatFunc = kbn.valueFormats[this.panel.format]; data.value = lastPoint[1]; data.valueRounded = data.value; - data.valueFormatted = formatFunc(data.value, this.dashboard.isTimezoneUtc()); + data.valueFormatted = formatFunc(data.value, 0, 0, this.dashboard.isTimezoneUtc()); } else { data.value = this.series[0].stats[this.panel.valueName]; data.flotpairs = this.series[0].flotpairs; const decimalInfo = this.getDecimalsForValue(data.value); const formatFunc = kbn.valueFormats[this.panel.format]; - data.valueFormatted = formatFunc(data.value, decimalInfo.decimals, decimalInfo.scaledDecimals); + + data.valueFormatted = formatFunc( + data.value, + decimalInfo.decimals, + decimalInfo.scaledDecimals, + this.dashboard.isTimezoneUtc() + ); data.valueRounded = kbn.roundValue(data.value, decimalInfo.decimals); } From 0c8f0416a0696545f7ae43efcda075bd537b96e7 Mon Sep 17 00:00:00 2001 From: Dominik Prokop Date: Mon, 14 Jan 2019 14:47:41 +0100 Subject: [PATCH 24/26] Move panel width/height calculation to PanelChrome --- public/app/features/dashboard/dashgrid/PanelChrome.tsx | 6 ++++-- public/app/plugins/panel/gauge/GaugePanel.tsx | 5 ++--- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/public/app/features/dashboard/dashgrid/PanelChrome.tsx b/public/app/features/dashboard/dashgrid/PanelChrome.tsx index 84e11511453..46534cac065 100644 --- a/public/app/features/dashboard/dashgrid/PanelChrome.tsx +++ b/public/app/features/dashboard/dashgrid/PanelChrome.tsx @@ -19,6 +19,8 @@ import { DashboardModel } from '../dashboard_model'; import { PanelPlugin } from 'app/types'; import { TimeRange } from '@grafana/ui'; +import variables from 'sass/_variables.scss'; + export interface Props { panel: PanelModel; dashboard: DashboardModel; @@ -122,8 +124,8 @@ export class PanelChrome extends PureComponent { timeSeries={timeSeries} timeRange={timeRange} options={panel.getOptions(plugin.exports.PanelDefaults)} - width={width} - height={height - PANEL_HEADER_HEIGHT} + width={width - 2 * variables.panelHorizontalPadding } + height={height - PANEL_HEADER_HEIGHT - variables.panelVerticalPadding} renderCounter={renderCounter} />
diff --git a/public/app/plugins/panel/gauge/GaugePanel.tsx b/public/app/plugins/panel/gauge/GaugePanel.tsx index 3aedfcf1f67..38a88428be7 100644 --- a/public/app/plugins/panel/gauge/GaugePanel.tsx +++ b/public/app/plugins/panel/gauge/GaugePanel.tsx @@ -3,7 +3,6 @@ import { GaugeOptions, PanelProps, NullValueMode } from '@grafana/ui'; import { getTimeSeriesVMs } from 'app/viz/state/timeSeries'; import Gauge from 'app/viz/Gauge'; -import variables from 'sass/_variables.scss'; interface Props extends PanelProps {} @@ -20,8 +19,8 @@ export class GaugePanel extends PureComponent { ); } From b9a3239edbb379202192b6f14b689935b09c240e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Mon, 14 Jan 2019 15:01:47 +0100 Subject: [PATCH 25/26] removed old unused angular stuff, rename --- public/app/features/plugins/all.ts | 3 +- .../features/plugins/ds_dashboards_ctrl.ts | 42 ---- public/app/features/plugins/ds_edit_ctrl.ts | 223 ------------------ ...ader.tsx => variableQueryEditorLoader.tsx} | 0 4 files changed, 1 insertion(+), 267 deletions(-) delete mode 100644 public/app/features/plugins/ds_dashboards_ctrl.ts delete mode 100644 public/app/features/plugins/ds_edit_ctrl.ts rename public/app/features/plugins/{VariableQueryComponentLoader.tsx => variableQueryEditorLoader.tsx} (100%) diff --git a/public/app/features/plugins/all.ts b/public/app/features/plugins/all.ts index 757a9a2f491..4845a3a6cb8 100644 --- a/public/app/features/plugins/all.ts +++ b/public/app/features/plugins/all.ts @@ -1,7 +1,6 @@ import './plugin_edit_ctrl'; import './plugin_page_ctrl'; import './import_list/import_list'; -import './ds_edit_ctrl'; import './datasource_srv'; import './plugin_component'; -import './VariableQueryComponentLoader'; +import './variableQueryEditorLoader'; diff --git a/public/app/features/plugins/ds_dashboards_ctrl.ts b/public/app/features/plugins/ds_dashboards_ctrl.ts deleted file mode 100644 index 71639702e41..00000000000 --- a/public/app/features/plugins/ds_dashboards_ctrl.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { coreModule } from 'app/core/core'; -import { store } from 'app/store/store'; -import { getNavModel } from 'app/core/selectors/navModel'; -import { buildNavModel } from './state/navModel'; - -export class DataSourceDashboardsCtrl { - datasourceMeta: any; - navModel: any; - current: any; - - /** @ngInject */ - constructor(private backendSrv, private $routeParams) { - const state = store.getState(); - this.navModel = getNavModel(state.navIndex, 'datasources'); - - if (this.$routeParams.id) { - this.getDatasourceById(this.$routeParams.id); - } - } - - getDatasourceById(id) { - this.backendSrv - .get('/api/datasources/' + id) - .then(ds => { - this.current = ds; - }) - .then(this.getPluginInfo.bind(this)); - } - - updateNav() { - this.navModel = buildNavModel(this.current, this.datasourceMeta, 'datasource-dashboards'); - } - - getPluginInfo() { - return this.backendSrv.get('/api/plugins/' + this.current.type + '/settings').then(pluginInfo => { - this.datasourceMeta = pluginInfo; - this.updateNav(); - }); - } -} - -coreModule.controller('DataSourceDashboardsCtrl', DataSourceDashboardsCtrl); diff --git a/public/app/features/plugins/ds_edit_ctrl.ts b/public/app/features/plugins/ds_edit_ctrl.ts deleted file mode 100644 index d8e9f7d8e53..00000000000 --- a/public/app/features/plugins/ds_edit_ctrl.ts +++ /dev/null @@ -1,223 +0,0 @@ -import _ from 'lodash'; -import config from 'app/core/config'; -import { coreModule, appEvents } from 'app/core/core'; -import { store } from 'app/store/store'; -import { getNavModel } from 'app/core/selectors/navModel'; -import { buildNavModel } from './state/navModel'; - -let datasourceTypes = []; - -const defaults = { - name: '', - type: 'graphite', - url: '', - access: 'proxy', - jsonData: {}, - secureJsonFields: {}, - secureJsonData: {}, -}; - -let datasourceCreated = false; - -export class DataSourceEditCtrl { - isNew: boolean; - datasources: any[]; - current: any; - types: any; - testing: any; - datasourceMeta: any; - editForm: any; - gettingStarted: boolean; - navModel: any; - - /** @ngInject */ - constructor(private $q, private backendSrv, private $routeParams, private $location, private datasourceSrv) { - const state = store.getState(); - this.navModel = getNavModel(state.navIndex, 'datasources'); - this.datasources = []; - - this.loadDatasourceTypes().then(() => { - if (this.$routeParams.id) { - this.getDatasourceById(this.$routeParams.id); - } else { - this.initNewDatasourceModel(); - } - }); - } - - initNewDatasourceModel() { - this.isNew = true; - this.current = _.cloneDeep(defaults); - - // We are coming from getting started - if (this.$location.search().gettingstarted) { - this.gettingStarted = true; - this.current.isDefault = true; - } - - this.typeChanged(); - } - - loadDatasourceTypes() { - if (datasourceTypes.length > 0) { - this.types = datasourceTypes; - return this.$q.when(null); - } - - return this.backendSrv.get('/api/plugins', { enabled: 1, type: 'datasource' }).then(plugins => { - datasourceTypes = plugins; - this.types = plugins; - }); - } - - getDatasourceById(id) { - this.backendSrv.get('/api/datasources/' + id).then(ds => { - this.isNew = false; - this.current = ds; - - if (datasourceCreated) { - datasourceCreated = false; - this.testDatasource(); - } - - return this.typeChanged(); - }); - } - - userChangedType() { - // reset model but keep name & default flag - this.current = _.defaults( - { - id: this.current.id, - name: this.current.name, - isDefault: this.current.isDefault, - type: this.current.type, - }, - _.cloneDeep(defaults) - ); - this.typeChanged(); - } - - updateNav() { - this.navModel = buildNavModel(this.current, this.datasourceMeta, 'datasource-settings'); - } - - typeChanged() { - return this.backendSrv.get('/api/plugins/' + this.current.type + '/settings').then(pluginInfo => { - this.datasourceMeta = pluginInfo; - this.updateNav(); - }); - } - - updateFrontendSettings() { - return this.backendSrv.get('/api/frontend/settings').then(settings => { - config.datasources = settings.datasources; - config.defaultDatasource = settings.defaultDatasource; - this.datasourceSrv.init(); - }); - } - - testDatasource() { - return this.datasourceSrv.get(this.current.name).then(datasource => { - if (!datasource.testDatasource) { - return; - } - - this.testing = { done: false, status: 'error' }; - - // make test call in no backend cache context - return this.backendSrv - .withNoBackendCache(() => { - return datasource - .testDatasource() - .then(result => { - this.testing.message = result.message; - this.testing.status = result.status; - }) - .catch(err => { - if (err.statusText) { - this.testing.message = 'HTTP Error ' + err.statusText; - } else { - this.testing.message = err.message; - } - }); - }) - .finally(() => { - this.testing.done = true; - }); - }); - } - - saveChanges() { - if (!this.editForm.$valid) { - return; - } - - if (this.current.readOnly) { - return; - } - - if (this.current.id) { - return this.backendSrv.put('/api/datasources/' + this.current.id, this.current).then(result => { - this.current = result.datasource; - this.updateNav(); - return this.updateFrontendSettings().then(() => { - return this.testDatasource(); - }); - }); - } else { - return this.backendSrv.post('/api/datasources', this.current).then(result => { - this.current = result.datasource; - this.updateFrontendSettings(); - - datasourceCreated = true; - this.$location.path('datasources/edit/' + result.id); - }); - } - } - - confirmDelete() { - this.backendSrv.delete('/api/datasources/' + this.current.id).then(() => { - this.$location.path('datasources'); - }); - } - - delete(s) { - appEvents.emit('confirm-modal', { - title: 'Delete', - text: 'Are you sure you want to delete this datasource?', - yesText: 'Delete', - icon: 'fa-trash', - onConfirm: () => { - this.confirmDelete(); - }, - }); - } -} - -coreModule.controller('DataSourceEditCtrl', DataSourceEditCtrl); - -coreModule.directive('datasourceHttpSettings', () => { - return { - scope: { - current: '=', - suggestUrl: '@', - noDirectAccess: '@', - }, - templateUrl: 'public/app/features/plugins/partials/ds_http_settings.html', - link: { - pre: ($scope, elem, attrs) => { - // do not show access option if direct access is disabled - $scope.showAccessOption = $scope.noDirectAccess !== 'true'; - $scope.showAccessHelp = false; - $scope.toggleAccessHelp = () => { - $scope.showAccessHelp = !$scope.showAccessHelp; - }; - - $scope.getSuggestUrls = () => { - return [$scope.suggestUrl]; - }; - }, - }, - }; -}); diff --git a/public/app/features/plugins/VariableQueryComponentLoader.tsx b/public/app/features/plugins/variableQueryEditorLoader.tsx similarity index 100% rename from public/app/features/plugins/VariableQueryComponentLoader.tsx rename to public/app/features/plugins/variableQueryEditorLoader.tsx From 827a292777aab76b28f17fb9c5fd5f7e74805fae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Mon, 14 Jan 2019 15:17:48 +0100 Subject: [PATCH 26/26] Moved panel editing components to it's own folder --- public/app/features/alerting/AlertTab.tsx | 2 +- .../dashboard/dashgrid/DashboardPanel.tsx | 2 +- .../dashboard/dashgrid/KeyboardNavigation.tsx | 71 ------------------- .../dashboard/dashgrid/PanelLoader.ts | 31 -------- .../DataSourceOption.tsx | 0 .../EditorTabBody.tsx | 0 .../{dashgrid => panel_editor}/GeneralTab.tsx | 0 .../PanelEditor.tsx | 0 .../{dashgrid => panel_editor}/QueriesTab.tsx | 0 .../QueryInspector.tsx | 0 .../QueryOptions.tsx | 0 .../VisualizationTab.tsx | 0 .../VizTypePicker.tsx | 0 .../VizTypePickerPlugin.tsx | 0 14 files changed, 2 insertions(+), 104 deletions(-) delete mode 100644 public/app/features/dashboard/dashgrid/KeyboardNavigation.tsx delete mode 100644 public/app/features/dashboard/dashgrid/PanelLoader.ts rename public/app/features/dashboard/{dashgrid => panel_editor}/DataSourceOption.tsx (100%) rename public/app/features/dashboard/{dashgrid => panel_editor}/EditorTabBody.tsx (100%) rename public/app/features/dashboard/{dashgrid => panel_editor}/GeneralTab.tsx (100%) rename public/app/features/dashboard/{dashgrid => panel_editor}/PanelEditor.tsx (100%) rename public/app/features/dashboard/{dashgrid => panel_editor}/QueriesTab.tsx (100%) rename public/app/features/dashboard/{dashgrid => panel_editor}/QueryInspector.tsx (100%) rename public/app/features/dashboard/{dashgrid => panel_editor}/QueryOptions.tsx (100%) rename public/app/features/dashboard/{dashgrid => panel_editor}/VisualizationTab.tsx (100%) rename public/app/features/dashboard/{dashgrid => panel_editor}/VizTypePicker.tsx (100%) rename public/app/features/dashboard/{dashgrid => panel_editor}/VizTypePickerPlugin.tsx (100%) diff --git a/public/app/features/alerting/AlertTab.tsx b/public/app/features/alerting/AlertTab.tsx index 2a1b3d12ecf..549d84c2808 100644 --- a/public/app/features/alerting/AlertTab.tsx +++ b/public/app/features/alerting/AlertTab.tsx @@ -6,7 +6,7 @@ import { AngularComponent, getAngularLoader } from 'app/core/services/AngularLoa import appEvents from 'app/core/app_events'; // Components -import { EditorTabBody, EditorToolbarView } from '../dashboard/dashgrid/EditorTabBody'; +import { EditorTabBody, EditorToolbarView } from '../dashboard/panel_editor/EditorTabBody'; import EmptyListCTA from 'app/core/components/EmptyListCTA/EmptyListCTA'; import StateHistory from './StateHistory'; import 'app/features/alerting/AlertTabCtrl'; diff --git a/public/app/features/dashboard/dashgrid/DashboardPanel.tsx b/public/app/features/dashboard/dashgrid/DashboardPanel.tsx index 9de298e0799..f0e97162d43 100644 --- a/public/app/features/dashboard/dashgrid/DashboardPanel.tsx +++ b/public/app/features/dashboard/dashgrid/DashboardPanel.tsx @@ -9,7 +9,7 @@ import { AddPanelPanel } from './AddPanelPanel'; import { getPanelPluginNotFound } from './PanelPluginNotFound'; import { DashboardRow } from './DashboardRow'; import { PanelChrome } from './PanelChrome'; -import { PanelEditor } from './PanelEditor'; +import { PanelEditor } from '../panel_editor/PanelEditor'; import { PanelModel } from '../panel_model'; import { DashboardModel } from '../dashboard_model'; diff --git a/public/app/features/dashboard/dashgrid/KeyboardNavigation.tsx b/public/app/features/dashboard/dashgrid/KeyboardNavigation.tsx deleted file mode 100644 index dab8371c925..00000000000 --- a/public/app/features/dashboard/dashgrid/KeyboardNavigation.tsx +++ /dev/null @@ -1,71 +0,0 @@ -import React, { KeyboardEvent, Component } from 'react'; - -interface State { - selected: number; -} - -export interface KeyboardNavigationProps { - onKeyDown: (evt: KeyboardEvent, maxSelectedIndex: number, onEnterAction: () => void) => void; - onMouseEnter: (select: number) => void; - selected: number; -} - -interface Props { - render: (injectProps: any) => void; -} - -class KeyboardNavigation extends Component { - constructor(props) { - super(props); - - this.state = { - selected: 0, - }; - } - - goToNext = (maxSelectedIndex: number) => { - const nextIndex = this.state.selected >= maxSelectedIndex ? 0 : this.state.selected + 1; - this.setState({ - selected: nextIndex, - }); - }; - - goToPrev = (maxSelectedIndex: number) => { - const nextIndex = this.state.selected <= 0 ? maxSelectedIndex : this.state.selected - 1; - this.setState({ - selected: nextIndex, - }); - }; - - onKeyDown = (evt: KeyboardEvent, maxSelectedIndex: number, onEnterAction: any) => { - if (evt.key === 'ArrowDown') { - evt.preventDefault(); - this.goToNext(maxSelectedIndex); - } - if (evt.key === 'ArrowUp') { - evt.preventDefault(); - this.goToPrev(maxSelectedIndex); - } - if (evt.key === 'Enter' && onEnterAction) { - onEnterAction(); - } - }; - - onMouseEnter = (mouseEnterIndex: number) => { - this.setState({ - selected: mouseEnterIndex, - }); - }; - - render() { - const injectProps = { - onKeyDown: this.onKeyDown, - onMouseEnter: this.onMouseEnter, - selected: this.state.selected, - }; - - return <>{this.props.render({ ...injectProps })}; - } -} - -export default KeyboardNavigation; diff --git a/public/app/features/dashboard/dashgrid/PanelLoader.ts b/public/app/features/dashboard/dashgrid/PanelLoader.ts deleted file mode 100644 index c654b756085..00000000000 --- a/public/app/features/dashboard/dashgrid/PanelLoader.ts +++ /dev/null @@ -1,31 +0,0 @@ -import angular from 'angular'; -import coreModule from 'app/core/core_module'; - -export interface AttachedPanel { - destroy(); -} - -export class PanelLoader { - /** @ngInject */ - constructor(private $compile, private $rootScope) {} - - load(elem, panel, dashboard): AttachedPanel { - const template = ''; - const panelScope = this.$rootScope.$new(); - panelScope.panel = panel; - panelScope.dashboard = dashboard; - - const compiledElem = this.$compile(template)(panelScope); - const rootNode = angular.element(elem); - rootNode.append(compiledElem); - - return { - destroy: () => { - panelScope.$destroy(); - compiledElem.remove(); - }, - }; - } -} - -coreModule.service('panelLoader', PanelLoader); diff --git a/public/app/features/dashboard/dashgrid/DataSourceOption.tsx b/public/app/features/dashboard/panel_editor/DataSourceOption.tsx similarity index 100% rename from public/app/features/dashboard/dashgrid/DataSourceOption.tsx rename to public/app/features/dashboard/panel_editor/DataSourceOption.tsx diff --git a/public/app/features/dashboard/dashgrid/EditorTabBody.tsx b/public/app/features/dashboard/panel_editor/EditorTabBody.tsx similarity index 100% rename from public/app/features/dashboard/dashgrid/EditorTabBody.tsx rename to public/app/features/dashboard/panel_editor/EditorTabBody.tsx diff --git a/public/app/features/dashboard/dashgrid/GeneralTab.tsx b/public/app/features/dashboard/panel_editor/GeneralTab.tsx similarity index 100% rename from public/app/features/dashboard/dashgrid/GeneralTab.tsx rename to public/app/features/dashboard/panel_editor/GeneralTab.tsx diff --git a/public/app/features/dashboard/dashgrid/PanelEditor.tsx b/public/app/features/dashboard/panel_editor/PanelEditor.tsx similarity index 100% rename from public/app/features/dashboard/dashgrid/PanelEditor.tsx rename to public/app/features/dashboard/panel_editor/PanelEditor.tsx diff --git a/public/app/features/dashboard/dashgrid/QueriesTab.tsx b/public/app/features/dashboard/panel_editor/QueriesTab.tsx similarity index 100% rename from public/app/features/dashboard/dashgrid/QueriesTab.tsx rename to public/app/features/dashboard/panel_editor/QueriesTab.tsx diff --git a/public/app/features/dashboard/dashgrid/QueryInspector.tsx b/public/app/features/dashboard/panel_editor/QueryInspector.tsx similarity index 100% rename from public/app/features/dashboard/dashgrid/QueryInspector.tsx rename to public/app/features/dashboard/panel_editor/QueryInspector.tsx diff --git a/public/app/features/dashboard/dashgrid/QueryOptions.tsx b/public/app/features/dashboard/panel_editor/QueryOptions.tsx similarity index 100% rename from public/app/features/dashboard/dashgrid/QueryOptions.tsx rename to public/app/features/dashboard/panel_editor/QueryOptions.tsx diff --git a/public/app/features/dashboard/dashgrid/VisualizationTab.tsx b/public/app/features/dashboard/panel_editor/VisualizationTab.tsx similarity index 100% rename from public/app/features/dashboard/dashgrid/VisualizationTab.tsx rename to public/app/features/dashboard/panel_editor/VisualizationTab.tsx diff --git a/public/app/features/dashboard/dashgrid/VizTypePicker.tsx b/public/app/features/dashboard/panel_editor/VizTypePicker.tsx similarity index 100% rename from public/app/features/dashboard/dashgrid/VizTypePicker.tsx rename to public/app/features/dashboard/panel_editor/VizTypePicker.tsx diff --git a/public/app/features/dashboard/dashgrid/VizTypePickerPlugin.tsx b/public/app/features/dashboard/panel_editor/VizTypePickerPlugin.tsx similarity index 100% rename from public/app/features/dashboard/dashgrid/VizTypePickerPlugin.tsx rename to public/app/features/dashboard/panel_editor/VizTypePickerPlugin.tsx