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/91] 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/91] 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/91] 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/91] 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 de4e1a91f7d6b42c008e7e1bafac2b194f2d9fc6 Mon Sep 17 00:00:00 2001 From: Dominik Prokop Date: Tue, 8 Jan 2019 13:44:10 +0100 Subject: [PATCH 05/91] Refactored withPoper HOC to PopperController using render prop --- public/app/core/components/Tooltip/Popper.tsx | 17 ++-- .../components/Tooltip/PopperController.tsx | 94 +++++++++++++++++++ .../core/components/Tooltip/withPopper.tsx | 88 ----------------- 3 files changed, 100 insertions(+), 99 deletions(-) create mode 100644 public/app/core/components/Tooltip/PopperController.tsx delete mode 100644 public/app/core/components/Tooltip/withPopper.tsx diff --git a/public/app/core/components/Tooltip/Popper.tsx b/public/app/core/components/Tooltip/Popper.tsx index 36cf0fe837e..cbd00028e90 100644 --- a/public/app/core/components/Tooltip/Popper.tsx +++ b/public/app/core/components/Tooltip/Popper.tsx @@ -1,6 +1,7 @@ import React, { PureComponent } from 'react'; +import * as PopperJS from 'popper.js'; +import { Manager, Popper as ReactPopper } from 'react-popper'; import Portal from 'app/core/components/Portal/Portal'; -import { Manager, Popper as ReactPopper, Reference } from 'react-popper'; import Transition from 'react-transition-group/Transition'; const defaultTransitionStyles = { @@ -18,29 +19,23 @@ const transitionStyles = { interface Props { renderContent: (content: any) => any; show: boolean; - placement?: any; + placement?: PopperJS.Placement; content: string | ((props: any) => JSX.Element); refClassName?: string; + referenceElement: PopperJS.ReferenceObject; } class Popper extends PureComponent { render() { - const { children, renderContent, show, placement, refClassName } = this.props; + const { renderContent, show, placement } = this.props; const { content } = this.props; return ( - - {({ ref }) => ( -
- {children} -
- )} -
{transitionState => ( - + {({ ref, style, placement, arrowProps }) => { return (
JSX.Element); + +export interface UsingPopperProps { + show?: boolean; + placement?: PopperJS.Placement; + content: PopperContent; + children: JSX.Element; + renderContent?: (content: PopperContent) => JSX.Element; +} + +type PopperControllerRenderProp = ( + showPopper: () => void, + hidePopper: () => void, + popperProps: { + show: boolean; + placement: PopperJS.Placement; + content: string | ((props: any) => JSX.Element); + renderContent: (content: any) => any; + } +) => JSX.Element; + +interface Props { + placement?: PopperJS.Placement; + content: PopperContent; + className?: string; + children: PopperControllerRenderProp; +} + +interface State { + placement: PopperJS.Placement; + show: boolean; +} + +class PopperController extends React.Component { + constructor(props: Props) { + super(props); + + this.state = { + placement: this.props.placement || 'auto', + show: false, + }; + } + + componentWillReceiveProps(nextProps: Props) { + if (nextProps.placement && nextProps.placement !== this.state.placement) { + this.setState(prevState => { + return { + ...prevState, + placement: nextProps.placement, + }; + }); + } + } + + showPopper = () => { + this.setState(prevState => ({ + ...prevState, + show: true, + })); + }; + + hidePopper = () => { + this.setState(prevState => ({ + ...prevState, + show: false, + })); + }; + + renderContent(content: PopperContent) { + if (typeof content === 'function') { + // If it's a function we assume it's a React component + const ReactComponent = content; + return ; + } + return content; + } + + render() { + const { children, content } = this.props; + const { show, placement } = this.state; + + return children(this.showPopper, this.hidePopper, { + show, + placement, + content, + renderContent: this.renderContent, + }); + } +} + +export default PopperController; diff --git a/public/app/core/components/Tooltip/withPopper.tsx b/public/app/core/components/Tooltip/withPopper.tsx deleted file mode 100644 index 4ba05937531..00000000000 --- a/public/app/core/components/Tooltip/withPopper.tsx +++ /dev/null @@ -1,88 +0,0 @@ -import React from 'react'; - -export interface UsingPopperProps { - showPopper: (prevState: object) => void; - hidePopper: (prevState: object) => void; - renderContent: (content: any) => any; - show: boolean; - placement?: string; - content: string | ((props: any) => JSX.Element); - className?: string; - refClassName?: string; -} - -interface Props { - placement?: string; - className?: string; - refClassName?: string; - content: string | ((props: any) => JSX.Element); -} - -interface State { - placement: string; - show: boolean; -} - -export default function withPopper(WrappedComponent) { - return class extends React.Component { - constructor(props) { - super(props); - this.setState = this.setState.bind(this); - this.state = { - placement: this.props.placement || 'auto', - show: false, - }; - } - - componentWillReceiveProps(nextProps) { - if (nextProps.placement && nextProps.placement !== this.state.placement) { - this.setState(prevState => { - return { - ...prevState, - placement: nextProps.placement, - }; - }); - } - } - - showPopper = () => { - this.setState(prevState => ({ - ...prevState, - show: true, - })); - }; - - hidePopper = () => { - this.setState(prevState => ({ - ...prevState, - show: false, - })); - }; - - renderContent(content) { - if (typeof content === 'function') { - // If it's a function we assume it's a React component - const ReactComponent = content; - return ; - } - return content; - } - - render() { - const { show, placement } = this.state; - const className = this.props.className || ''; - - return ( - - ); - } - }; -} From ec904cf66247a870f49d4ccaf0ef52f2f476c30d Mon Sep 17 00:00:00 2001 From: Dominik Prokop Date: Tue, 8 Jan 2019 13:53:59 +0100 Subject: [PATCH 06/91] Update components to fit updated PopperController API --- public/app/core/components/Label/Label.tsx | 6 ++- .../app/core/components/Tooltip/Popover.tsx | 19 ---------- .../core/components/Tooltip/Tooltip.test.tsx | 4 +- .../app/core/components/Tooltip/Tooltip.tsx | 37 ++++++++++++------- .../__snapshots__/Tooltip.test.tsx.snap | 17 +++------ .../dashboard/dashgrid/PanelEditor.tsx | 2 +- .../PanelHeader/PanelHeaderCorner.tsx | 10 +++-- .../permissions/DashboardPermissions.tsx | 6 ++- .../features/folders/FolderPermissions.tsx | 6 ++- public/app/features/teams/TeamGroupSync.tsx | 6 ++- public/app/features/teams/TeamSettings.tsx | 1 + .../__snapshots__/TeamGroupSync.test.tsx.snap | 30 +++++++++------ 12 files changed, 73 insertions(+), 71 deletions(-) delete mode 100644 public/app/core/components/Tooltip/Popover.tsx diff --git a/public/app/core/components/Label/Label.tsx b/public/app/core/components/Label/Label.tsx index 362c3c577f7..956678283f6 100644 --- a/public/app/core/components/Label/Label.tsx +++ b/public/app/core/components/Label/Label.tsx @@ -14,8 +14,10 @@ export const Label: SFC = props => { {props.children} {props.tooltip && ( - - + +
+ +
)}
diff --git a/public/app/core/components/Tooltip/Popover.tsx b/public/app/core/components/Tooltip/Popover.tsx deleted file mode 100644 index 62397243c1c..00000000000 --- a/public/app/core/components/Tooltip/Popover.tsx +++ /dev/null @@ -1,19 +0,0 @@ -import React, { PureComponent } from 'react'; -import Popper from './Popper'; -import withPopper, { UsingPopperProps } from './withPopper'; - -class Popover extends PureComponent { - render() { - const { children, hidePopper, showPopper, className, ...restProps } = this.props; - - const togglePopper = restProps.show ? hidePopper : showPopper; - - return ( -
- {children} -
- ); - } -} - -export default withPopper(Popover); diff --git a/public/app/core/components/Tooltip/Tooltip.test.tsx b/public/app/core/components/Tooltip/Tooltip.test.tsx index d2c96bb23d2..4a6def738e0 100644 --- a/public/app/core/components/Tooltip/Tooltip.test.tsx +++ b/public/app/core/components/Tooltip/Tooltip.test.tsx @@ -6,8 +6,8 @@ describe('Tooltip', () => { it('renders correctly', () => { const tree = renderer .create( - - Link with tooltip + + Link with tooltip ) .toJSON(); diff --git a/public/app/core/components/Tooltip/Tooltip.tsx b/public/app/core/components/Tooltip/Tooltip.tsx index 795da94a03c..a905be7d988 100644 --- a/public/app/core/components/Tooltip/Tooltip.tsx +++ b/public/app/core/components/Tooltip/Tooltip.tsx @@ -1,17 +1,28 @@ -import React, { PureComponent } from 'react'; +import React, { createRef } from 'react'; +import * as PopperJS from 'popper.js'; + import Popper from './Popper'; -import withPopper, { UsingPopperProps } from './withPopper'; +import PopperController, { UsingPopperProps } from './PopperController'; -class Tooltip extends PureComponent { - render() { - const { children, hidePopper, showPopper, className, ...restProps } = this.props; +const Tooltip = ({ children, renderContent, ...controllerProps }: UsingPopperProps) => { + const tooltipTriggerRef = createRef(); - return ( -
- {children} -
- ); - } -} + return ( + + {(showPopper, hidePopper, popperProps) => { + return ( + <> + + {React.cloneElement(children, { + ref: tooltipTriggerRef, + onMouseEnter: showPopper, + onMouseLeave: hidePopper, + })} + + ); + }} + + ); +}; -export default withPopper(Tooltip); +export default Tooltip; diff --git a/public/app/core/components/Tooltip/__snapshots__/Tooltip.test.tsx.snap b/public/app/core/components/Tooltip/__snapshots__/Tooltip.test.tsx.snap index c7d680049f4..761221906d4 100644 --- a/public/app/core/components/Tooltip/__snapshots__/Tooltip.test.tsx.snap +++ b/public/app/core/components/Tooltip/__snapshots__/Tooltip.test.tsx.snap @@ -1,19 +1,12 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`Tooltip renders correctly 1`] = ` - + Link with tooltip + `; diff --git a/public/app/features/dashboard/dashgrid/PanelEditor.tsx b/public/app/features/dashboard/dashgrid/PanelEditor.tsx index fbc683c2eb3..bce9af252ee 100644 --- a/public/app/features/dashboard/dashgrid/PanelEditor.tsx +++ b/public/app/features/dashboard/dashgrid/PanelEditor.tsx @@ -138,7 +138,7 @@ function TabItem({ tab, activeTab, onClick }: TabItemParams) { return (
onClick(tab)}> - + diff --git a/public/app/features/dashboard/dashgrid/PanelHeader/PanelHeaderCorner.tsx b/public/app/features/dashboard/dashgrid/PanelHeader/PanelHeaderCorner.tsx index 331e469a60d..3346f4b902d 100644 --- a/public/app/features/dashboard/dashgrid/PanelHeader/PanelHeaderCorner.tsx +++ b/public/app/features/dashboard/dashgrid/PanelHeader/PanelHeaderCorner.tsx @@ -78,12 +78,14 @@ export class PanelHeaderCorner extends Component { {infoMode === InfoModes.Info || infoMode === InfoModes.Links ? ( - - +
+ + +
) : null} diff --git a/public/app/features/dashboard/permissions/DashboardPermissions.tsx b/public/app/features/dashboard/permissions/DashboardPermissions.tsx index c07bef42930..95d78e7a737 100644 --- a/public/app/features/dashboard/permissions/DashboardPermissions.tsx +++ b/public/app/features/dashboard/permissions/DashboardPermissions.tsx @@ -70,8 +70,10 @@ export class DashboardPermissions extends PureComponent {

Permissions

- - + +
+ +
- - ) - .toJSON(); - expect(tree).toMatchSnapshot(); - }); -}); diff --git a/public/app/core/components/Tooltip/__snapshots__/Popover.test.tsx.snap b/public/app/core/components/Tooltip/__snapshots__/Popover.test.tsx.snap deleted file mode 100644 index b36a4fe9af9..00000000000 --- a/public/app/core/components/Tooltip/__snapshots__/Popover.test.tsx.snap +++ /dev/null @@ -1,16 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`Popover renders correctly 1`] = ` -
-
- -
-
-`; From de3876997311b984f68add424893e5a2988a3181 Mon Sep 17 00:00:00 2001 From: Peter Holmberg Date: Tue, 8 Jan 2019 15:25:23 +0100 Subject: [PATCH 08/91] 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 09/91] 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 22c9ce7de827ef9078449aecec69786eb9bbae7e Mon Sep 17 00:00:00 2001 From: Dominik Prokop Date: Tue, 8 Jan 2019 17:01:50 +0100 Subject: [PATCH 10/91] Make tooltips persistent when hovered --- public/app/core/components/Tooltip/Popper.tsx | 6 ++++-- public/app/core/components/Tooltip/Tooltip.tsx | 7 ++++++- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/public/app/core/components/Tooltip/Popper.tsx b/public/app/core/components/Tooltip/Popper.tsx index cbd00028e90..dc89b801e37 100644 --- a/public/app/core/components/Tooltip/Popper.tsx +++ b/public/app/core/components/Tooltip/Popper.tsx @@ -16,7 +16,7 @@ const transitionStyles = { exiting: { opacity: 0 }, }; -interface Props { +interface Props extends React.DOMAttributes { renderContent: (content: any) => any; show: boolean; placement?: PopperJS.Placement; @@ -27,7 +27,7 @@ interface Props { class Popper extends PureComponent { render() { - const { renderContent, show, placement } = this.props; + const { renderContent, show, placement, onMouseEnter, onMouseLeave } = this.props; const { content } = this.props; return ( @@ -39,6 +39,8 @@ class Popper extends PureComponent { {({ ref, style, placement, arrowProps }) => { return (
{ return ( <> - + {React.cloneElement(children, { ref: tooltipTriggerRef, onMouseEnter: showPopper, From 79c6fdc0e8fe7bb900a88c3782df8f8870d8a5c1 Mon Sep 17 00:00:00 2001 From: Dominik Prokop Date: Tue, 8 Jan 2019 20:51:00 +0100 Subject: [PATCH 11/91] wip --- packages/grafana-ui/package.json | 6 ++++-- .../src}/components/Tooltip/Popper.tsx | 0 .../components/Tooltip/PopperController.tsx | 0 .../src}/components/Tooltip/Tooltip.test.tsx | 8 +++++--- .../src}/components/Tooltip/Tooltip.tsx | 19 +++++++++---------- .../src/components/Tooltip/_Tooltip.scss | 0 .../__snapshots__/Tooltip.test.tsx.snap | 0 packages/grafana-ui/src/components/index.scss | 1 + packages/grafana-ui/src/components/index.ts | 1 + public/app/core/components/Label/Label.tsx | 2 +- .../ToggleButtonGroup/ToggleButtonGroup.tsx | 2 +- .../dashboard/dashgrid/DataSourceOption.tsx | 2 +- .../dashboard/dashgrid/PanelEditor.tsx | 2 +- .../PanelHeader/PanelHeaderCorner.tsx | 6 +++--- .../permissions/DashboardPermissions.tsx | 2 +- .../features/folders/FolderPermissions.tsx | 2 +- public/app/features/teams/TeamGroupSync.tsx | 2 +- public/sass/_grafana.scss | 1 - yarn.lock | 18 ++++++++++++++++-- 19 files changed, 46 insertions(+), 28 deletions(-) rename {public/app/core => packages/grafana-ui/src}/components/Tooltip/Popper.tsx (100%) rename {public/app/core => packages/grafana-ui/src}/components/Tooltip/PopperController.tsx (100%) rename {public/app/core => packages/grafana-ui/src}/components/Tooltip/Tooltip.test.tsx (69%) rename {public/app/core => packages/grafana-ui/src}/components/Tooltip/Tooltip.tsx (63%) rename public/sass/components/_popper.scss => packages/grafana-ui/src/components/Tooltip/_Tooltip.scss (100%) rename {public/app/core => packages/grafana-ui/src}/components/Tooltip/__snapshots__/Tooltip.test.tsx.snap (100%) diff --git a/packages/grafana-ui/package.json b/packages/grafana-ui/package.json index 2fb210e3b46..f48b8221f9c 100644 --- a/packages/grafana-ui/package.json +++ b/packages/grafana-ui/package.json @@ -11,6 +11,8 @@ "license": "ISC", "dependencies": { "@torkelo/react-select": "2.1.1", + "@types/react-test-renderer": "^16.0.3", + "@types/react-transition-group": "^2.0.15", "classnames": "^2.2.5", "jquery": "^3.2.1", "lodash": "^4.17.10", @@ -23,11 +25,11 @@ "react-virtualized": "^9.21.0" }, "devDependencies": { + "@types/classnames": "^2.2.6", "@types/jest": "^23.3.2", + "@types/jquery": "^1.10.35", "@types/lodash": "^4.14.119", "@types/react": "^16.7.6", - "@types/classnames": "^2.2.6", - "@types/jquery": "^1.10.35", "typescript": "^3.2.2" } } diff --git a/public/app/core/components/Tooltip/Popper.tsx b/packages/grafana-ui/src/components/Tooltip/Popper.tsx similarity index 100% rename from public/app/core/components/Tooltip/Popper.tsx rename to packages/grafana-ui/src/components/Tooltip/Popper.tsx diff --git a/public/app/core/components/Tooltip/PopperController.tsx b/packages/grafana-ui/src/components/Tooltip/PopperController.tsx similarity index 100% rename from public/app/core/components/Tooltip/PopperController.tsx rename to packages/grafana-ui/src/components/Tooltip/PopperController.tsx diff --git a/public/app/core/components/Tooltip/Tooltip.test.tsx b/packages/grafana-ui/src/components/Tooltip/Tooltip.test.tsx similarity index 69% rename from public/app/core/components/Tooltip/Tooltip.test.tsx rename to packages/grafana-ui/src/components/Tooltip/Tooltip.test.tsx index 4a6def738e0..95d01c7f2fe 100644 --- a/public/app/core/components/Tooltip/Tooltip.test.tsx +++ b/packages/grafana-ui/src/components/Tooltip/Tooltip.test.tsx @@ -1,13 +1,15 @@ import React from 'react'; import renderer from 'react-test-renderer'; -import Tooltip from './Tooltip'; +import { Tooltip } from './Tooltip'; describe('Tooltip', () => { it('renders correctly', () => { const tree = renderer .create( - - Link with tooltip + + + Link with tooltip + ) .toJSON(); diff --git a/public/app/core/components/Tooltip/Tooltip.tsx b/packages/grafana-ui/src/components/Tooltip/Tooltip.tsx similarity index 63% rename from public/app/core/components/Tooltip/Tooltip.tsx rename to packages/grafana-ui/src/components/Tooltip/Tooltip.tsx index 8fcc4793ba9..9cffb151d83 100644 --- a/public/app/core/components/Tooltip/Tooltip.tsx +++ b/packages/grafana-ui/src/components/Tooltip/Tooltip.tsx @@ -1,10 +1,9 @@ import React, { createRef } from 'react'; import * as PopperJS from 'popper.js'; - import Popper from './Popper'; import PopperController, { UsingPopperProps } from './PopperController'; -const Tooltip = ({ children, renderContent, ...controllerProps }: UsingPopperProps) => { +export const Tooltip = ({ children, renderContent, ...controllerProps }: UsingPopperProps) => { const tooltipTriggerRef = createRef(); return ( @@ -12,12 +11,14 @@ const Tooltip = ({ children, renderContent, ...controllerProps }: UsingPopperPro {(showPopper, hidePopper, popperProps) => { return ( <> - + {tooltipTriggerRef.current && ( + + )} {React.cloneElement(children, { ref: tooltipTriggerRef, onMouseEnter: showPopper, @@ -29,5 +30,3 @@ const Tooltip = ({ children, renderContent, ...controllerProps }: UsingPopperPro ); }; - -export default Tooltip; diff --git a/public/sass/components/_popper.scss b/packages/grafana-ui/src/components/Tooltip/_Tooltip.scss similarity index 100% rename from public/sass/components/_popper.scss rename to packages/grafana-ui/src/components/Tooltip/_Tooltip.scss diff --git a/public/app/core/components/Tooltip/__snapshots__/Tooltip.test.tsx.snap b/packages/grafana-ui/src/components/Tooltip/__snapshots__/Tooltip.test.tsx.snap similarity index 100% rename from public/app/core/components/Tooltip/__snapshots__/Tooltip.test.tsx.snap rename to packages/grafana-ui/src/components/Tooltip/__snapshots__/Tooltip.test.tsx.snap diff --git a/packages/grafana-ui/src/components/index.scss b/packages/grafana-ui/src/components/index.scss index d52508c946c..fd10d21f9f1 100644 --- a/packages/grafana-ui/src/components/index.scss +++ b/packages/grafana-ui/src/components/index.scss @@ -1 +1,2 @@ @import 'DeleteButton/DeleteButton'; +@import 'Tooltip/Tooltip'; diff --git a/packages/grafana-ui/src/components/index.ts b/packages/grafana-ui/src/components/index.ts index b57b9bcfdb7..8b9b11404c2 100644 --- a/packages/grafana-ui/src/components/index.ts +++ b/packages/grafana-ui/src/components/index.ts @@ -1 +1,2 @@ export { DeleteButton } from './DeleteButton/DeleteButton'; +export { Tooltip } from './Tooltip/Tooltip'; diff --git a/public/app/core/components/Label/Label.tsx b/public/app/core/components/Label/Label.tsx index 956678283f6..5d60efa056a 100644 --- a/public/app/core/components/Label/Label.tsx +++ b/public/app/core/components/Label/Label.tsx @@ -1,5 +1,5 @@ import React, { SFC, ReactNode } from 'react'; -import Tooltip from '../Tooltip/Tooltip'; +import { Tooltip } from '@grafana/ui'; interface Props { tooltip?: string; diff --git a/public/app/core/components/ToggleButtonGroup/ToggleButtonGroup.tsx b/public/app/core/components/ToggleButtonGroup/ToggleButtonGroup.tsx index 2524a265054..86e15923bda 100644 --- a/public/app/core/components/ToggleButtonGroup/ToggleButtonGroup.tsx +++ b/public/app/core/components/ToggleButtonGroup/ToggleButtonGroup.tsx @@ -1,5 +1,5 @@ import React, { SFC, ReactNode, PureComponent } from 'react'; -import Tooltip from 'app/core/components/Tooltip/Tooltip'; +import { Tooltip } from '@grafana/ui'; interface ToggleButtonGroupProps { label?: string; diff --git a/public/app/features/dashboard/dashgrid/DataSourceOption.tsx b/public/app/features/dashboard/dashgrid/DataSourceOption.tsx index 0adfc4abe16..9a3ce527510 100644 --- a/public/app/features/dashboard/dashgrid/DataSourceOption.tsx +++ b/public/app/features/dashboard/dashgrid/DataSourceOption.tsx @@ -1,5 +1,5 @@ import React, { SFC } from 'react'; -import Tooltip from 'app/core/components/Tooltip/Tooltip'; +import { Tooltip } from '@grafana/ui'; interface Props { label: string; diff --git a/public/app/features/dashboard/dashgrid/PanelEditor.tsx b/public/app/features/dashboard/dashgrid/PanelEditor.tsx index bce9af252ee..22921a4f98e 100644 --- a/public/app/features/dashboard/dashgrid/PanelEditor.tsx +++ b/public/app/features/dashboard/dashgrid/PanelEditor.tsx @@ -15,7 +15,7 @@ import { PanelModel } from '../panel_model'; import { DashboardModel } from '../dashboard_model'; import { PanelPlugin } from 'app/types/plugins'; -import Tooltip from 'app/core/components/Tooltip/Tooltip'; +import { Tooltip } from '@grafana/ui'; interface PanelEditorProps { panel: PanelModel; diff --git a/public/app/features/dashboard/dashgrid/PanelHeader/PanelHeaderCorner.tsx b/public/app/features/dashboard/dashgrid/PanelHeader/PanelHeaderCorner.tsx index 3346f4b902d..82b8d57b64a 100644 --- a/public/app/features/dashboard/dashgrid/PanelHeader/PanelHeaderCorner.tsx +++ b/public/app/features/dashboard/dashgrid/PanelHeader/PanelHeaderCorner.tsx @@ -1,10 +1,10 @@ import React, { Component } from 'react'; +import Remarkable from 'remarkable'; +import { Tooltip } from '@grafana/ui'; import { PanelModel } from 'app/features/dashboard/panel_model'; -import Tooltip from 'app/core/components/Tooltip/Tooltip'; import templateSrv from 'app/features/templating/template_srv'; import { LinkSrv } from 'app/features/dashboard/panellinks/link_srv'; import { getTimeSrv, TimeSrv } from 'app/features/dashboard/time_srv'; -import Remarkable from 'remarkable'; enum InfoModes { Error = 'Error', @@ -78,7 +78,7 @@ export class PanelHeaderCorner extends Component { {infoMode === InfoModes.Info || infoMode === InfoModes.Links ? (
Date: Mon, 20 Aug 2018 15:33:49 +0200 Subject: [PATCH 12/91] Max number of repeated panels per row Instead of min width --- docs/sources/reference/dashboard.md | 2 +- docs/sources/reference/templating.md | 6 ++++-- pkg/models/dashboards.go | 2 +- .../features/dashboard/dashboard_migration.ts | 11 +++++++++- .../app/features/dashboard/dashboard_model.ts | 4 ++-- public/app/features/dashboard/panel_model.ts | 2 +- .../specs/dashboard_migration.test.ts | 20 ++++++++++--------- .../features/panel/partials/general_tab.html | 4 ++-- public/dashboards/home.json | 2 +- 9 files changed, 33 insertions(+), 20 deletions(-) diff --git a/docs/sources/reference/dashboard.md b/docs/sources/reference/dashboard.md index 6be12600da5..3d96923bc72 100644 --- a/docs/sources/reference/dashboard.md +++ b/docs/sources/reference/dashboard.md @@ -51,7 +51,7 @@ When a user creates a new dashboard, a new dashboard JSON object is initialized "list": [] }, "refresh": "5s", - "schemaVersion": 16, + "schemaVersion": 17, "version": 0, "links": [] } diff --git a/docs/sources/reference/templating.md b/docs/sources/reference/templating.md index f20cc0ccfc9..71ce6bdd2ae 100644 --- a/docs/sources/reference/templating.md +++ b/docs/sources/reference/templating.md @@ -292,9 +292,11 @@ The `direction` controls how the panels will be arranged. By choosing `horizontal` the panels will be arranged side-by-side. Grafana will automatically adjust the width of each repeated panel so that the whole row is filled. Currently, you cannot mix other panels on a row with a repeated -panel. Each panel will never be smaller that the provided `Min width` if you have many selected values. +panel. -By choosing `vertical` the panels will be arranged from top to bottom in a column. The `Min width` doesn't have any effect in this case. The width of the repeated panels will be the same as of the first panel (the original template) being repeated. +Set `Max per row` to tell grafana how many panels per row you want at most. It defaults to *4* if you don't set anything. + +By choosing `vertical` the panels will be arranged from top to bottom in a column. The width of the repeated panels will be the same as of the first panel (the original template) being repeated. Only make changes to the first panel (the original template). To have the changes take effect on all panels you need to trigger a dynamic dashboard re-build. You can do this by either changing the variable value (that is the basis for the repeat) or reload the dashboard. diff --git a/pkg/models/dashboards.go b/pkg/models/dashboards.go index 3a8010e797b..0f3f56175fe 100644 --- a/pkg/models/dashboards.go +++ b/pkg/models/dashboards.go @@ -112,7 +112,7 @@ func NewDashboard(title string) *Dashboard { func NewDashboardFolder(title string) *Dashboard { folder := NewDashboard(title) folder.IsFolder = true - folder.Data.Set("schemaVersion", 16) + folder.Data.Set("schemaVersion", 17) folder.Data.Set("version", 0) folder.IsFolder = true return folder diff --git a/public/app/features/dashboard/dashboard_migration.ts b/public/app/features/dashboard/dashboard_migration.ts index abd12ab4b13..4196456907f 100644 --- a/public/app/features/dashboard/dashboard_migration.ts +++ b/public/app/features/dashboard/dashboard_migration.ts @@ -21,7 +21,7 @@ export class DashboardMigrator { let i, j, k, n; const oldVersion = this.dashboard.schemaVersion; const panelUpgrades = []; - this.dashboard.schemaVersion = 16; + this.dashboard.schemaVersion = 17; if (oldVersion === this.dashboard.schemaVersion) { return; @@ -368,6 +368,15 @@ export class DashboardMigrator { this.upgradeToGridLayout(old); } + if (oldVersion < 17) { + panelUpgrades.push(panel => { + if (panel.minSpan) { + panel.maxPerRow = GRID_COLUMN_COUNT / panel.minSpan; + } + delete panel.minSpan; + }); + } + if (panelUpgrades.length === 0) { return; } diff --git a/public/app/features/dashboard/dashboard_model.ts b/public/app/features/dashboard/dashboard_model.ts index 6f98bc5a17a..33529abdd15 100644 --- a/public/app/features/dashboard/dashboard_model.ts +++ b/public/app/features/dashboard/dashboard_model.ts @@ -442,7 +442,7 @@ export class DashboardModel { } const selectedOptions = this.getSelectedVariableOptions(variable); - const minWidth = panel.minSpan || 6; + const maxPerRow = panel.maxPerRow || 4; let xPos = 0; let yPos = panel.gridPos.y; @@ -462,7 +462,7 @@ export class DashboardModel { } else { // set width based on how many are selected // assumed the repeated panels should take up full row width - copy.gridPos.w = Math.max(GRID_COLUMN_COUNT / selectedOptions.length, minWidth); + copy.gridPos.w = Math.max(GRID_COLUMN_COUNT / selectedOptions.length, GRID_COLUMN_COUNT / maxPerRow); copy.gridPos.x = xPos; copy.gridPos.y = yPos; diff --git a/public/app/features/dashboard/panel_model.ts b/public/app/features/dashboard/panel_model.ts index 2d5a70b47dd..2fec8e379dd 100644 --- a/public/app/features/dashboard/panel_model.ts +++ b/public/app/features/dashboard/panel_model.ts @@ -77,7 +77,7 @@ export class PanelModel { repeatPanelId?: number; repeatDirection?: string; repeatedByRow?: boolean; - minSpan?: number; + maxPerRow?: number; collapsed?: boolean; panels?: any; soloMode?: boolean; diff --git a/public/app/features/dashboard/specs/dashboard_migration.test.ts b/public/app/features/dashboard/specs/dashboard_migration.test.ts index 5f693c9f6d9..e15bd65d5a5 100644 --- a/public/app/features/dashboard/specs/dashboard_migration.test.ts +++ b/public/app/features/dashboard/specs/dashboard_migration.test.ts @@ -127,7 +127,7 @@ describe('DashboardModel', () => { }); it('dashboard schema version should be set to latest', () => { - expect(model.schemaVersion).toBe(16); + expect(model.schemaVersion).toBe(17); }); it('graph thresholds should be migrated', () => { @@ -364,14 +364,6 @@ describe('DashboardModel', () => { expect(dashboard.panels.length).toBe(2); }); - it('minSpan should be twice', () => { - model.rows = [createRow({ height: 8 }, [[6]])]; - model.rows[0].panels[0] = { minSpan: 12 }; - - const dashboard = new DashboardModel(model); - expect(dashboard.panels[0].minSpan).toBe(24); - }); - it('should assign id', () => { model.rows = [createRow({ collapse: true, height: 8 }, [[6], [6]])]; model.rows[0].panels[0] = {}; @@ -380,6 +372,16 @@ describe('DashboardModel', () => { expect(dashboard.panels[0].id).toBe(1); }); }); + + describe('when migrating from minSpan to maxPerRow', () => { + it('maxPerRow should be correct', () => { + const model = { + panels: [{ minSpan: 8 }], + }; + const dashboard = new DashboardModel(model); + expect(dashboard.panels[0].maxPerRow).toBe(3); + }); + }); }); function createRow(options, panelDescriptions: any[]) { diff --git a/public/app/features/panel/partials/general_tab.html b/public/app/features/panel/partials/general_tab.html index d6c2d4804a0..76c38f73912 100644 --- a/public/app/features/panel/partials/general_tab.html +++ b/public/app/features/panel/partials/general_tab.html @@ -32,8 +32,8 @@
- Min width -
diff --git a/public/dashboards/home.json b/public/dashboards/home.json index 55cf7242aa6..f2c441053bb 100644 --- a/public/dashboards/home.json +++ b/public/dashboards/home.json @@ -65,7 +65,7 @@ } ], "rows": [], - "schemaVersion": 16, + "schemaVersion": 17, "style": "dark", "tags": [], "templating": { From fb76f41a15e5ee52bbb3b58229da12adcf3bf817 Mon Sep 17 00:00:00 2001 From: ijin08 Date: Wed, 9 Jan 2019 09:42:30 +0100 Subject: [PATCH 13/91] changed light theme page background gradient --- public/sass/_variables.light.scss | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/public/sass/_variables.light.scss b/public/sass/_variables.light.scss index 16bb341ba27..6ad07011b68 100644 --- a/public/sass/_variables.light.scss +++ b/public/sass/_variables.light.scss @@ -76,8 +76,7 @@ $textShadow: none; // gradients $brand-gradient: linear-gradient(to right, rgba(255, 213, 0, 1) 0%, rgba(255, 68, 0, 1) 99%, rgba(255, 68, 0, 1) 100%); -$page-gradient: linear-gradient(-60deg, $gray-7, #f5f6f9 70%, $gray-7 98%); -//$page-gradient: linear-gradient(180deg, $white 10px, $gray-7 100px); +$page-gradient: linear-gradient(180deg, $white 10px, $gray-7 100px); $edit-gradient: linear-gradient(-60deg, $gray-7, #f5f6f9 70%, $gray-7 98%); // Links From 6ac25d41fa29f02ba6437da365aa8d2b5b5d2a25 Mon Sep 17 00:00:00 2001 From: Johannes Schill Date: Tue, 8 Jan 2019 16:59:16 +0100 Subject: [PATCH 14/91] chore: Move CustomScrollbar to @grafana/ui #14759 --- package.json | 4 +-- packages/grafana-ui/package.json | 8 +++-- .../CustomScrollbar/CustomScrollbar.test.tsx | 0 .../CustomScrollbar/CustomScrollbar.tsx | 2 +- .../CustomScrollbar.test.tsx.snap | 0 packages/grafana-ui/src/components/index.ts | 1 + public/app/core/components/Select/Select.tsx | 2 +- .../dashboard/dashgrid/EditorTabBody.tsx | 2 +- .../app/plugins/panel/graph/Legend/Legend.tsx | 2 +- yarn.lock | 34 +++++++++++++++++-- 10 files changed, 44 insertions(+), 11 deletions(-) rename {public/app/core => packages/grafana-ui/src}/components/CustomScrollbar/CustomScrollbar.test.tsx (100%) rename {public/app/core => packages/grafana-ui/src}/components/CustomScrollbar/CustomScrollbar.tsx (96%) rename {public/app/core => packages/grafana-ui/src}/components/CustomScrollbar/__snapshots__/CustomScrollbar.test.tsx.snap (100%) diff --git a/package.json b/package.json index eefe2cbbe53..c8d891b91bc 100644 --- a/package.json +++ b/package.json @@ -24,7 +24,6 @@ "@types/jquery": "^1.10.35", "@types/node": "^8.0.31", "@types/react": "^16.7.6", - "@types/react-custom-scrollbars": "^4.0.5", "@types/react-dom": "^16.0.9", "@types/react-select": "^2.0.4", "angular-mocks": "1.6.6", @@ -72,8 +71,8 @@ "ng-annotate-loader": "^0.6.1", "ng-annotate-webpack-plugin": "^0.3.0", "ngtemplate-loader": "^2.0.1", - "npm": "^5.4.2", "node-sass": "^4.11.0", + "npm": "^5.4.2", "optimize-css-assets-webpack-plugin": "^4.0.2", "phantomjs-prebuilt": "^2.1.15", "postcss-browser-reporter": "^0.5.0", @@ -167,7 +166,6 @@ "prop-types": "^15.6.2", "rc-cascader": "^0.14.0", "react": "^16.6.3", - "react-custom-scrollbars": "^4.2.1", "react-dom": "^16.6.3", "react-grid-layout": "0.16.6", "react-highlight-words": "0.11.0", diff --git a/packages/grafana-ui/package.json b/packages/grafana-ui/package.json index 2fb210e3b46..594faa57b0a 100644 --- a/packages/grafana-ui/package.json +++ b/packages/grafana-ui/package.json @@ -16,6 +16,7 @@ "lodash": "^4.17.10", "moment": "^2.22.2", "react": "^16.6.3", + "react-custom-scrollbars": "^4.2.1", "react-dom": "^16.6.3", "react-highlight-words": "0.11.0", "react-popper": "^1.3.0", @@ -23,11 +24,14 @@ "react-virtualized": "^9.21.0" }, "devDependencies": { + "@types/classnames": "^2.2.6", "@types/jest": "^23.3.2", + "@types/jquery": "^1.10.35", "@types/lodash": "^4.14.119", "@types/react": "^16.7.6", - "@types/classnames": "^2.2.6", - "@types/jquery": "^1.10.35", + "@types/react-custom-scrollbars": "^4.0.5", + "@types/react-test-renderer": "^16.0.3", + "react-test-renderer": "^16.7.0", "typescript": "^3.2.2" } } diff --git a/public/app/core/components/CustomScrollbar/CustomScrollbar.test.tsx b/packages/grafana-ui/src/components/CustomScrollbar/CustomScrollbar.test.tsx similarity index 100% rename from public/app/core/components/CustomScrollbar/CustomScrollbar.test.tsx rename to packages/grafana-ui/src/components/CustomScrollbar/CustomScrollbar.test.tsx diff --git a/public/app/core/components/CustomScrollbar/CustomScrollbar.tsx b/packages/grafana-ui/src/components/CustomScrollbar/CustomScrollbar.tsx similarity index 96% rename from public/app/core/components/CustomScrollbar/CustomScrollbar.tsx rename to packages/grafana-ui/src/components/CustomScrollbar/CustomScrollbar.tsx index 977892c637d..cf1657e1c83 100644 --- a/public/app/core/components/CustomScrollbar/CustomScrollbar.tsx +++ b/packages/grafana-ui/src/components/CustomScrollbar/CustomScrollbar.tsx @@ -12,7 +12,7 @@ interface Props { /** * Wraps component into component from `react-custom-scrollbars` */ -class CustomScrollbar extends PureComponent { +export class CustomScrollbar extends PureComponent { static defaultProps: Partial = { customClassName: 'custom-scrollbars', autoHide: true, diff --git a/public/app/core/components/CustomScrollbar/__snapshots__/CustomScrollbar.test.tsx.snap b/packages/grafana-ui/src/components/CustomScrollbar/__snapshots__/CustomScrollbar.test.tsx.snap similarity index 100% rename from public/app/core/components/CustomScrollbar/__snapshots__/CustomScrollbar.test.tsx.snap rename to packages/grafana-ui/src/components/CustomScrollbar/__snapshots__/CustomScrollbar.test.tsx.snap diff --git a/packages/grafana-ui/src/components/index.ts b/packages/grafana-ui/src/components/index.ts index b57b9bcfdb7..7423ce2a93a 100644 --- a/packages/grafana-ui/src/components/index.ts +++ b/packages/grafana-ui/src/components/index.ts @@ -1 +1,2 @@ export { DeleteButton } from './DeleteButton/DeleteButton'; +export { CustomScrollbar } from './CustomScrollbar/CustomScrollbar'; diff --git a/public/app/core/components/Select/Select.tsx b/public/app/core/components/Select/Select.tsx index 893eb1a6655..f66e07c9ed6 100644 --- a/public/app/core/components/Select/Select.tsx +++ b/public/app/core/components/Select/Select.tsx @@ -11,7 +11,7 @@ import OptionGroup from './OptionGroup'; import IndicatorsContainer from './IndicatorsContainer'; import NoOptionsMessage from './NoOptionsMessage'; import ResetStyles from './ResetStyles'; -import CustomScrollbar from '../CustomScrollbar/CustomScrollbar'; +import { CustomScrollbar } from '@grafana/ui'; export interface SelectOptionItem { label?: string; diff --git a/public/app/features/dashboard/dashgrid/EditorTabBody.tsx b/public/app/features/dashboard/dashgrid/EditorTabBody.tsx index b7da81a23f8..b159cb30a4b 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 from 'app/core/components/CustomScrollbar/CustomScrollbar'; +import { CustomScrollbar } from '@grafana/ui'; import { FadeIn } from 'app/core/components/Animations/FadeIn'; import { PanelOptionSection } from './PanelOptionSection'; diff --git a/public/app/plugins/panel/graph/Legend/Legend.tsx b/public/app/plugins/panel/graph/Legend/Legend.tsx index 7af61fde4e9..b83cd7bd88c 100644 --- a/public/app/plugins/panel/graph/Legend/Legend.tsx +++ b/public/app/plugins/panel/graph/Legend/Legend.tsx @@ -1,7 +1,7 @@ import _ from 'lodash'; import React, { PureComponent } from 'react'; import { TimeSeries } from 'app/core/core'; -import CustomScrollbar from 'app/core/components/CustomScrollbar/CustomScrollbar'; +import { CustomScrollbar } from '@grafana/ui'; import { LegendItem, LEGEND_STATS } from './LegendSeriesItem'; interface LegendProps { diff --git a/yarn.lock b/yarn.lock index 78300be233e..0f7cf0fab92 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1091,6 +1091,13 @@ "@types/react-dom" "*" "@types/react-transition-group" "*" +"@types/react-test-renderer@^16.0.3": + version "16.0.3" + resolved "https://registry.yarnpkg.com/@types/react-test-renderer/-/react-test-renderer-16.0.3.tgz#cce5c983d66cc5c3582e7c2f44b274ab635a8acc" + integrity sha512-NWOAxVQeJxpXuNKgw83Hah0nquiw1nUexM9qY/Hk3a+XhZwgMtaa6GLA9E1TKMT75Odb3/KE/jiBO4enTuEJjQ== + dependencies: + "@types/react" "*" + "@types/react-transition-group@*": version "2.0.14" resolved "https://registry.yarnpkg.com/@types/react-transition-group/-/react-transition-group-2.0.14.tgz#afd0cd785a97f070b55765e9f9d76ff568269001" @@ -1098,7 +1105,7 @@ dependencies: "@types/react" "*" -"@types/react@*", "@types/react@16.7.6", "@types/react@^16.1.0", "@types/react@^16.7.6": +"@types/react@*", "@types/react@^16.1.0", "@types/react@^16.7.6": version "16.7.6" resolved "https://registry.yarnpkg.com/@types/react/-/react-16.7.6.tgz#80e4bab0d0731ad3ae51f320c4b08bdca5f03040" integrity sha512-QBUfzftr/8eg/q3ZRgf/GaDP6rTYc7ZNem+g4oZM38C9vXyV8AWRWaTQuW5yCoZTsfHrN7b3DeEiUnqH9SrnpA== @@ -3161,7 +3168,7 @@ caniuse-api@^1.5.2: lodash.memoize "^4.1.2" lodash.uniq "^4.5.0" -caniuse-db@1.0.30000772, caniuse-db@^1.0.30000529, caniuse-db@^1.0.30000634, caniuse-db@^1.0.30000639: +caniuse-db@^1.0.30000529, caniuse-db@^1.0.30000634, caniuse-db@^1.0.30000639: version "1.0.30000772" resolved "https://registry.yarnpkg.com/caniuse-db/-/caniuse-db-1.0.30000772.tgz#51aae891768286eade4a3d8319ea76d6a01b512b" integrity sha1-UarokXaChureSj2DGep21qAbUSs= @@ -12061,6 +12068,11 @@ react-is@^16.5.2: resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.5.2.tgz#e2a7b7c3f5d48062eb769fcb123505eb928722e3" integrity sha512-hSl7E6l25GTjNEZATqZIuWOgSnpXb3kD0DVCujmg46K5zLxsbiKaaT6VO9slkSBDPZfYs30lwfJwbOFOnoEnKQ== +react-is@^16.7.0: + version "16.7.0" + resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.7.0.tgz#c1bd21c64f1f1364c6f70695ec02d69392f41bfa" + integrity sha512-Z0VRQdF4NPDoI0tsXVMLkJLiwEBa+RP66g0xDHxgxysxSoCUccSten4RTF/UFvZF1dZvZ9Zu1sx+MDXwcOR34g== + react-lifecycles-compat@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz#4f1a273afdfc8f3488a8c516bfda78f872352362" @@ -12134,6 +12146,16 @@ react-test-renderer@^16.0.0-0, react-test-renderer@^16.5.0: react-is "^16.5.2" schedule "^0.5.0" +react-test-renderer@^16.7.0: + version "16.7.0" + resolved "https://registry.yarnpkg.com/react-test-renderer/-/react-test-renderer-16.7.0.tgz#1ca96c2b450ab47c36ba92cd8c03fcefc52ea01c" + integrity sha512-tFbhSjknSQ6+ttzmuGdv+SjQfmvGcq3PFKyPItohwhhOBmRoTf1We3Mlt3rJtIn85mjPXOkKV+TaKK4irvk9Yg== + dependencies: + object-assign "^4.1.1" + prop-types "^15.6.2" + react-is "^16.7.0" + scheduler "^0.12.0" + react-transition-group@^2.2.1: version "2.5.0" resolved "https://registry.yarnpkg.com/react-transition-group/-/react-transition-group-2.5.0.tgz#70bca0e3546102c4dc5cf3f5f57f73447cce6874" @@ -12977,6 +12999,14 @@ scheduler@^0.11.2: loose-envify "^1.1.0" object-assign "^4.1.1" +scheduler@^0.12.0: + version "0.12.0" + resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.12.0.tgz#8ab17699939c0aedc5a196a657743c496538647b" + integrity sha512-t7MBR28Akcp4Jm+QoR63XgAi9YgCUmgvDHqf5otgAj4QvdoBE4ImCX0ffehefePPG+aitiYHp0g/mW6s4Tp+dw== + dependencies: + loose-envify "^1.1.0" + object-assign "^4.1.1" + schema-utils@^0.4.0, schema-utils@^0.4.4, schema-utils@^0.4.5: version "0.4.7" resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-0.4.7.tgz#ba74f597d2be2ea880131746ee17d0a093c68187" From 076defdc0b1b6e119c9c619967cafab3682cdee3 Mon Sep 17 00:00:00 2001 From: Dominik Prokop Date: Wed, 9 Jan 2019 10:33:20 +0100 Subject: [PATCH 15/91] Post merge updates --- .../features/dashboard/dashgrid/DataPanel.tsx | 25 ++++++++----------- .../PanelHeader/PanelHeaderCorner.tsx | 2 +- 2 files changed, 12 insertions(+), 15 deletions(-) diff --git a/public/app/features/dashboard/dashgrid/DataPanel.tsx b/public/app/features/dashboard/dashgrid/DataPanel.tsx index fa21276d7a2..d71a274ab10 100644 --- a/public/app/features/dashboard/dashgrid/DataPanel.tsx +++ b/public/app/features/dashboard/dashgrid/DataPanel.tsx @@ -1,6 +1,8 @@ // Library import React, { Component } from 'react'; -import Tooltip from 'app/core/components/Tooltip/Tooltip'; +import { Tooltip } from '@grafana/ui'; +import { Themes } from '@grafana/ui/src/components/Tooltip/Popper'; + import ErrorBoundary from 'app/core/components/ErrorBoundary/ErrorBoundary'; // Services @@ -12,7 +14,6 @@ import kbn from 'app/core/utils/kbn'; // Types import { DataQueryOptions, DataQueryResponse } from 'app/types'; import { TimeRange, TimeSeries, LoadingState } from '@grafana/ui'; -import { Themes } from 'app/core/components/Tooltip/Popper'; const DEFAULT_PLUGIN_ERROR = 'Error in plugin'; @@ -144,10 +145,10 @@ export class DataPanel extends Component { this.setState({ loading: LoadingState.Error, isFirstLoad: false, - errorMessage: errorMessage + errorMessage: errorMessage, }); } - } + }; render() { const { queries } = this.props; @@ -171,7 +172,7 @@ export class DataPanel extends Component { <> {this.renderLoadingStates()} - {({error, errorInfo}) => { + {({ error, errorInfo }) => { if (errorInfo) { this.onError(error.message || DEFAULT_PLUGIN_ERROR); return null; @@ -200,15 +201,11 @@ export class DataPanel extends Component { ); } else if (loading === LoadingState.Error) { return ( - - - + +
+ + +
); } diff --git a/public/app/features/dashboard/dashgrid/PanelHeader/PanelHeaderCorner.tsx b/public/app/features/dashboard/dashgrid/PanelHeader/PanelHeaderCorner.tsx index 82b8d57b64a..6b6f81fc579 100644 --- a/public/app/features/dashboard/dashgrid/PanelHeader/PanelHeaderCorner.tsx +++ b/public/app/features/dashboard/dashgrid/PanelHeader/PanelHeaderCorner.tsx @@ -78,7 +78,7 @@ export class PanelHeaderCorner extends Component { {infoMode === InfoModes.Info || infoMode === InfoModes.Links ? (
Date: Wed, 9 Jan 2019 10:35:03 +0100 Subject: [PATCH 16/91] chore: Move sass code related to custom scrollbar into @grafana/ui #14759 --- .../CustomScrollbar/_CustomScrollbar.scss | 40 +++++++++++++++++ packages/grafana-ui/src/components/index.scss | 1 + public/sass/components/_scrollbar.scss | 44 ------------------- 3 files changed, 41 insertions(+), 44 deletions(-) create mode 100644 packages/grafana-ui/src/components/CustomScrollbar/_CustomScrollbar.scss diff --git a/packages/grafana-ui/src/components/CustomScrollbar/_CustomScrollbar.scss b/packages/grafana-ui/src/components/CustomScrollbar/_CustomScrollbar.scss new file mode 100644 index 00000000000..c0a8077fb63 --- /dev/null +++ b/packages/grafana-ui/src/components/CustomScrollbar/_CustomScrollbar.scss @@ -0,0 +1,40 @@ +.custom-scrollbars { + // Fix for Firefox. For some reason sometimes .view container gets a height of its content, but in order to + // make scroll working it should fit outer container size (scroll appears only when inner container size is + // greater than outer one). + display: flex; + flex-grow: 1; + + .view { + display: flex; + flex-grow: 1; + flex-direction: column; + } + + .track-vertical { + border-radius: 3px; + width: 6px !important; + right: 2px; + bottom: 2px; + top: 2px; + } + + .track-horizontal { + border-radius: 3px; + height: 6px !important; + + right: 2px; + bottom: 2px; + left: 2px; + } + + .thumb-vertical { + @include gradient-vertical($scrollbarBackground, $scrollbarBackground2); + border-radius: 6px; + } + + .thumb-horizontal { + @include gradient-horizontal($scrollbarBackground, $scrollbarBackground2); + border-radius: 6px; + } +} \ No newline at end of file diff --git a/packages/grafana-ui/src/components/index.scss b/packages/grafana-ui/src/components/index.scss index d52508c946c..0e18eaf018c 100644 --- a/packages/grafana-ui/src/components/index.scss +++ b/packages/grafana-ui/src/components/index.scss @@ -1 +1,2 @@ +@import 'CustomScrollbar/CustomScrollbar'; @import 'DeleteButton/DeleteButton'; diff --git a/public/sass/components/_scrollbar.scss b/public/sass/components/_scrollbar.scss index 5dbb4518d52..a7ecb73b786 100644 --- a/public/sass/components/_scrollbar.scss +++ b/public/sass/components/_scrollbar.scss @@ -295,50 +295,6 @@ } } -// Custom styles for 'react-custom-scrollbars' - -.custom-scrollbars { - // Fix for Firefox. For some reason sometimes .view container gets a height of its content, but in order to - // make scroll working it should fit outer container size (scroll appears only when inner container size is - // greater than outer one). - display: flex; - flex-grow: 1; - - .view { - display: flex; - flex-grow: 1; - flex-direction: column; - } - - .track-vertical { - border-radius: 3px; - width: 6px !important; - - right: 2px; - bottom: 2px; - top: 2px; - } - - .track-horizontal { - border-radius: 3px; - height: 6px !important; - - right: 2px; - bottom: 2px; - left: 2px; - } - - .thumb-vertical { - @include gradient-vertical($scrollbarBackground, $scrollbarBackground2); - border-radius: 6px; - } - - .thumb-horizontal { - @include gradient-horizontal($scrollbarBackground, $scrollbarBackground2); - border-radius: 6px; - } -} - .scroll-margin-helper { margin-right: 12px; } From fae8ff57500fa7d9deb20395a7495034861562ba Mon Sep 17 00:00:00 2001 From: Dominik Prokop Date: Wed, 9 Jan 2019 10:55:38 +0100 Subject: [PATCH 17/91] Move Portal to @grafana/ui --- .../grafana-ui/src}/components/Portal/Portal.tsx | 4 ++-- packages/grafana-ui/src/components/Tooltip/Popper.tsx | 4 ++-- packages/grafana-ui/src/components/index.ts | 1 + 3 files changed, 5 insertions(+), 4 deletions(-) rename {public/app/core => packages/grafana-ui/src}/components/Portal/Portal.tsx (87%) diff --git a/public/app/core/components/Portal/Portal.tsx b/packages/grafana-ui/src/components/Portal/Portal.tsx similarity index 87% rename from public/app/core/components/Portal/Portal.tsx rename to packages/grafana-ui/src/components/Portal/Portal.tsx index 25d54a64209..6f51f4053e2 100644 --- a/public/app/core/components/Portal/Portal.tsx +++ b/packages/grafana-ui/src/components/Portal/Portal.tsx @@ -6,11 +6,11 @@ interface Props { root?: HTMLElement; } -export default class BodyPortal extends PureComponent { +export class Portal extends PureComponent { node: HTMLElement = document.createElement('div'); portalRoot: HTMLElement; - constructor(props) { + constructor(props: Props) { super(props); const { className, diff --git a/packages/grafana-ui/src/components/Tooltip/Popper.tsx b/packages/grafana-ui/src/components/Tooltip/Popper.tsx index fd12e7db517..c393ed4bac5 100644 --- a/packages/grafana-ui/src/components/Tooltip/Popper.tsx +++ b/packages/grafana-ui/src/components/Tooltip/Popper.tsx @@ -1,7 +1,7 @@ -import React, { PureComponent } from 'react'; +import React, { PureComponent } from 'react'; import * as PopperJS from 'popper.js'; import { Manager, Popper as ReactPopper } from 'react-popper'; -import Portal from 'app/core/components/Portal/Portal'; +import { Portal } from '@grafana/ui'; import Transition from 'react-transition-group/Transition'; export enum Themes { diff --git a/packages/grafana-ui/src/components/index.ts b/packages/grafana-ui/src/components/index.ts index 8b9b11404c2..d1205e6c291 100644 --- a/packages/grafana-ui/src/components/index.ts +++ b/packages/grafana-ui/src/components/index.ts @@ -1,2 +1,3 @@ export { DeleteButton } from './DeleteButton/DeleteButton'; export { Tooltip } from './Tooltip/Tooltip'; +export { Portal } from './Portal/Portal'; From 0b4d212bd2fe8a9ac198ae9478791356d277023b Mon Sep 17 00:00:00 2001 From: Dominik Prokop Date: Wed, 9 Jan 2019 10:56:40 +0100 Subject: [PATCH 18/91] Fixing TS errors and updating snapshot --- packages/grafana-ui/src/components/Tooltip/Popper.tsx | 4 ++-- .../src/components/Tooltip/PopperController.tsx | 4 ++-- .../teams/__snapshots__/TeamGroupSync.test.tsx.snap | 8 ++++---- yarn.lock | 4 ++-- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/packages/grafana-ui/src/components/Tooltip/Popper.tsx b/packages/grafana-ui/src/components/Tooltip/Popper.tsx index c393ed4bac5..b405d4c4328 100644 --- a/packages/grafana-ui/src/components/Tooltip/Popper.tsx +++ b/packages/grafana-ui/src/components/Tooltip/Popper.tsx @@ -1,4 +1,4 @@ -import React, { PureComponent } from 'react'; +import React, { PureComponent } from 'react'; import * as PopperJS from 'popper.js'; import { Manager, Popper as ReactPopper } from 'react-popper'; import { Portal } from '@grafana/ui'; @@ -14,7 +14,7 @@ const defaultTransitionStyles = { opacity: 0, }; -const transitionStyles = { +const transitionStyles: {[key: string]: object} = { exited: { opacity: 0 }, entering: { opacity: 0 }, entered: { opacity: 1 }, diff --git a/packages/grafana-ui/src/components/Tooltip/PopperController.tsx b/packages/grafana-ui/src/components/Tooltip/PopperController.tsx index 1b6703c3627..5f4010ac58a 100644 --- a/packages/grafana-ui/src/components/Tooltip/PopperController.tsx +++ b/packages/grafana-ui/src/components/Tooltip/PopperController.tsx @@ -50,10 +50,10 @@ class PopperController extends React.Component { componentWillReceiveProps(nextProps: Props) { if (nextProps.placement && nextProps.placement !== this.state.placement) { - this.setState(prevState => { + this.setState((prevState: State) => { return { ...prevState, - placement: nextProps.placement, + placement: nextProps.placement || 'auto', }; }); } diff --git a/public/app/features/teams/__snapshots__/TeamGroupSync.test.tsx.snap b/public/app/features/teams/__snapshots__/TeamGroupSync.test.tsx.snap index ac26dba88ed..e28eb86dc7a 100644 --- a/public/app/features/teams/__snapshots__/TeamGroupSync.test.tsx.snap +++ b/public/app/features/teams/__snapshots__/TeamGroupSync.test.tsx.snap @@ -10,7 +10,7 @@ exports[`Render should render component 1`] = ` > External group sync - @@ -21,7 +21,7 @@ exports[`Render should render component 1`] = ` className="gicon gicon-question gicon--has-hover" />
-
+
@@ -119,7 +119,7 @@ exports[`Render should render groups table 1`] = ` > External group sync - @@ -130,7 +130,7 @@ exports[`Render should render groups table 1`] = ` className="gicon gicon-question gicon--has-hover" />
-
+
diff --git a/yarn.lock b/yarn.lock index ae8c1dc5e06..23ebebb689c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1112,7 +1112,7 @@ dependencies: "@types/react" "*" -"@types/react@*", "@types/react@^16.1.0", "@types/react@^16.7.6": +"@types/react@*", "@types/react@16.7.6", "@types/react@^16.1.0", "@types/react@^16.7.6": version "16.7.6" resolved "https://registry.yarnpkg.com/@types/react/-/react-16.7.6.tgz#80e4bab0d0731ad3ae51f320c4b08bdca5f03040" integrity sha512-QBUfzftr/8eg/q3ZRgf/GaDP6rTYc7ZNem+g4oZM38C9vXyV8AWRWaTQuW5yCoZTsfHrN7b3DeEiUnqH9SrnpA== @@ -3175,7 +3175,7 @@ caniuse-api@^1.5.2: lodash.memoize "^4.1.2" lodash.uniq "^4.5.0" -caniuse-db@^1.0.30000529, caniuse-db@^1.0.30000634, caniuse-db@^1.0.30000639: +caniuse-db@1.0.30000772, caniuse-db@^1.0.30000529, caniuse-db@^1.0.30000634, caniuse-db@^1.0.30000639: version "1.0.30000772" resolved "https://registry.yarnpkg.com/caniuse-db/-/caniuse-db-1.0.30000772.tgz#51aae891768286eade4a3d8319ea76d6a01b512b" integrity sha1-UarokXaChureSj2DGep21qAbUSs= From 97b087f5a561019392bb21053a64e07c91bbbba2 Mon Sep 17 00:00:00 2001 From: Pierre GIRAUD Date: Tue, 21 Aug 2018 09:22:41 +0200 Subject: [PATCH 19/91] Use factors for max repeated panels per row --- public/app/core/specs/factors.test.ts | 8 ++++++++ public/app/core/utils/factors.ts | 5 +++++ public/app/features/dashboard/dashboard_migration.ts | 12 +++++++++++- public/app/features/panel/panel_ctrl.ts | 5 ++++- 4 files changed, 28 insertions(+), 2 deletions(-) create mode 100644 public/app/core/specs/factors.test.ts create mode 100644 public/app/core/utils/factors.ts diff --git a/public/app/core/specs/factors.test.ts b/public/app/core/specs/factors.test.ts new file mode 100644 index 00000000000..aed59b5be8b --- /dev/null +++ b/public/app/core/specs/factors.test.ts @@ -0,0 +1,8 @@ +import getFactors from 'app/core/utils/factors'; + +describe('factors', () => { + it('should return factors for 12', () => { + const factors = getFactors(12); + expect(factors).toEqual([1, 2, 3, 4, 6, 12]); + }); +}); diff --git a/public/app/core/utils/factors.ts b/public/app/core/utils/factors.ts new file mode 100644 index 00000000000..e9ce327a631 --- /dev/null +++ b/public/app/core/utils/factors.ts @@ -0,0 +1,5 @@ +// Returns the factors of a number +// Example getFactors(12) -> [1, 2, 3, 4, 6, 12] +export default function getFactors(num: number): number[] { + return Array.from(new Array(num + 1), (_, i) => i).filter(i => num % i === 0); +} diff --git a/public/app/features/dashboard/dashboard_migration.ts b/public/app/features/dashboard/dashboard_migration.ts index 4196456907f..2dbeb6c6e80 100644 --- a/public/app/features/dashboard/dashboard_migration.ts +++ b/public/app/features/dashboard/dashboard_migration.ts @@ -9,6 +9,7 @@ import { } from 'app/core/constants'; import { PanelModel } from './panel_model'; import { DashboardModel } from './dashboard_model'; +import getFactors from 'app/core/utils/factors'; export class DashboardMigrator { dashboard: DashboardModel; @@ -371,7 +372,16 @@ export class DashboardMigrator { if (oldVersion < 17) { panelUpgrades.push(panel => { if (panel.minSpan) { - panel.maxPerRow = GRID_COLUMN_COUNT / panel.minSpan; + const max = GRID_COLUMN_COUNT / panel.minSpan; + const factors = getFactors(GRID_COLUMN_COUNT); + // find the best match compared to factors + // (ie. [1,2,3,4,6,12,24] for 24 columns) + panel.maxPerRow = + factors[ + _.findIndex(factors, o => { + return o > max; + }) - 1 + ]; } delete panel.minSpan; }); diff --git a/public/app/features/panel/panel_ctrl.ts b/public/app/features/panel/panel_ctrl.ts index 432d22fecdf..f68423315d7 100644 --- a/public/app/features/panel/panel_ctrl.ts +++ b/public/app/features/panel/panel_ctrl.ts @@ -5,6 +5,7 @@ import Remarkable from 'remarkable'; import config from 'app/core/config'; import { profiler } from 'app/core/core'; import { Emitter } from 'app/core/core'; +import getFactors from 'app/core/utils/factors'; import { duplicatePanel, copyPanel as copyPanelUtil, @@ -12,7 +13,7 @@ import { sharePanel as sharePanelUtil, } from 'app/features/dashboard/utils/panel'; -import { GRID_CELL_HEIGHT, GRID_CELL_VMARGIN, PANEL_HEADER_HEIGHT, PANEL_BORDER } from 'app/core/constants'; +import { GRID_CELL_HEIGHT, GRID_CELL_VMARGIN, GRID_COLUMN_COUNT, PANEL_HEADER_HEIGHT, PANEL_BORDER } from 'app/core/constants'; export class PanelCtrl { panel: any; @@ -32,6 +33,7 @@ export class PanelCtrl { events: Emitter; timing: any; loading: boolean; + maxPanelsPerRowOptions: number[]; constructor($scope, $injector) { this.$injector = $injector; @@ -92,6 +94,7 @@ export class PanelCtrl { if (!this.editModeInitiated) { this.editModeInitiated = true; this.events.emit('init-edit-mode', null); + this.maxPanelsPerRowOptions = getFactors(GRID_COLUMN_COUNT); } } From 0571ad5ad7db0d9facf2f8c3e5d5b62f3ba2fa25 Mon Sep 17 00:00:00 2001 From: Dominik Prokop Date: Wed, 9 Jan 2019 11:33:08 +0100 Subject: [PATCH 20/91] Removes unnecessary warnings from webpack output about missing exports This should not break anything as ForkTsCheckerWebpackPlugin takes care of that --- scripts/webpack/IgnoreNotFoundExportPlugin.js | 22 +++++++++++++++++++ scripts/webpack/webpack.hot.js | 2 ++ 2 files changed, 24 insertions(+) create mode 100644 scripts/webpack/IgnoreNotFoundExportPlugin.js diff --git a/scripts/webpack/IgnoreNotFoundExportPlugin.js b/scripts/webpack/IgnoreNotFoundExportPlugin.js new file mode 100644 index 00000000000..37bf1f7200e --- /dev/null +++ b/scripts/webpack/IgnoreNotFoundExportPlugin.js @@ -0,0 +1,22 @@ +// https://github.com/TypeStrong/ts-loader/issues/653#issuecomment-390889335 + +const ModuleDependencyWarning = require("webpack/lib/ModuleDependencyWarning") + +module.exports = class IgnoreNotFoundExportPlugin { + apply(compiler) { + const messageRegExp = /export '.*'( \(reexported as '.*'\))? was not found in/ + function doneHook(stats) { + stats.compilation.warnings = stats.compilation.warnings.filter(function(warn) { + if (warn instanceof ModuleDependencyWarning && messageRegExp.test(warn.message)) { + return false + } + return true; + }) + } + if (compiler.hooks) { + compiler.hooks.done.tap("IgnoreNotFoundExportPlugin", doneHook) + } else { + compiler.plugin("done", doneHook) + } + } +} diff --git a/scripts/webpack/webpack.hot.js b/scripts/webpack/webpack.hot.js index ca08ff1e726..b37e4c08592 100644 --- a/scripts/webpack/webpack.hot.js +++ b/scripts/webpack/webpack.hot.js @@ -7,6 +7,7 @@ const webpack = require('webpack'); const HtmlWebpackPlugin = require('html-webpack-plugin'); const HtmlWebpackHarddiskPlugin = require('html-webpack-harddisk-plugin'); const CleanWebpackPlugin = require('clean-webpack-plugin'); +const IgnoreNotFoundExportPlugin = require("./IgnoreNotFoundExportPlugin.js"); module.exports = merge(common, { entry: { @@ -111,5 +112,6 @@ module.exports = merge(common, { NODE_ENV: JSON.stringify('development'), }, }), + new IgnoreNotFoundExportPlugin(), ], }); From f374da032e5cc9a12646c596298a1d8d38200206 Mon Sep 17 00:00:00 2001 From: Pierre GIRAUD Date: Wed, 9 Jan 2019 11:34:13 +0100 Subject: [PATCH 21/91] Hint for user on when the repeat is applied --- public/app/features/panel/partials/general_tab.html | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/public/app/features/panel/partials/general_tab.html b/public/app/features/panel/partials/general_tab.html index 76c38f73912..8881d2c28a4 100644 --- a/public/app/features/panel/partials/general_tab.html +++ b/public/app/features/panel/partials/general_tab.html @@ -37,7 +37,12 @@
+
+
+ Note: You may need to change the variable selection to see this in action. +
+
From e2fe663dba0922810bc56165023db4846024289f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hugo=20H=C3=A4ggmark?= Date: Wed, 9 Jan 2019 11:34:22 +0100 Subject: [PATCH 22/91] Added TestRuleButton --- public/app/features/alerting/AlertTab.tsx | 29 +++++++++-- .../app/features/alerting/TestRuleButton.tsx | 48 +++++++++++++++++++ 2 files changed, 72 insertions(+), 5 deletions(-) create mode 100644 public/app/features/alerting/TestRuleButton.tsx diff --git a/public/app/features/alerting/AlertTab.tsx b/public/app/features/alerting/AlertTab.tsx index a5afbc198fc..5623fac95c1 100644 --- a/public/app/features/alerting/AlertTab.tsx +++ b/public/app/features/alerting/AlertTab.tsx @@ -1,5 +1,5 @@ // Libraries -import React, { PureComponent } from 'react'; +import React, { PureComponent, SFC } from 'react'; // Services & Utils import { AngularComponent, getAngularLoader } from 'app/core/services/AngularLoader'; @@ -14,6 +14,7 @@ import 'app/features/alerting/AlertTabCtrl'; // Types import { DashboardModel } from '../dashboard/dashboard_model'; import { PanelModel } from '../dashboard/panel_model'; +import { TestRuleButton } from './TestRuleButton'; interface Props { angularPanel?: AngularComponent; @@ -21,6 +22,16 @@ interface Props { panel: PanelModel; } +interface LoadingPlaceholderProps { + text: string; +} + +const LoadingPlaceholder: SFC = ({ text }) => ( +
+ {text} +
+); + export class AlertTab extends PureComponent { element: any; component: AngularComponent; @@ -65,9 +76,7 @@ export class AlertTab extends PureComponent { const loader = getAngularLoader(); const template = ''; - const scopeProps = { - ctrl: this.panelCtrl, - }; + const scopeProps = { ctrl: this.panelCtrl }; this.component = loader.load(this.element, scopeProps, template); } @@ -111,6 +120,16 @@ export class AlertTab extends PureComponent { }; }; + renderTestRuleButton = () => { + const { panel, dashboard } = this.props; + return ; + }; + + testRule = (): EditorToolbarView => ({ + title: 'Test Rule', + render: () => this.renderTestRuleButton(), + }); + onAddAlert = () => { this.panelCtrl._enableAlert(); this.component.digest(); @@ -120,7 +139,7 @@ export class AlertTab extends PureComponent { render() { const { alert } = this.props.panel; - const toolbarItems = alert ? [this.stateHistory(), this.deleteAlert()] : []; + const toolbarItems = alert ? [this.stateHistory(), this.testRule(), this.deleteAlert()] : []; const model = { title: 'Panel has no alert rule defined', diff --git a/public/app/features/alerting/TestRuleButton.tsx b/public/app/features/alerting/TestRuleButton.tsx new file mode 100644 index 00000000000..032d7c7e4f8 --- /dev/null +++ b/public/app/features/alerting/TestRuleButton.tsx @@ -0,0 +1,48 @@ +import React, { PureComponent } from 'react'; +import { JSONFormatter } from 'app/core/components/JSONFormatter/JSONFormatter'; +import { getBackendSrv } from 'app/core/services/backend_srv'; +import { DashboardModel } from '../dashboard/dashboard_model'; + +export interface Props { + panelId: number; + dashboard: DashboardModel; + LoadingPlaceholder: any; +} + +interface State { + isLoading: boolean; + testRuleResponse: {}; +} + +export class TestRuleButton extends PureComponent { + constructor(props) { + super(props); + this.state = { isLoading: false, testRuleResponse: {} }; + } + + componentDidMount() { + this.testRule(); + } + + async testRule() { + const { panelId, dashboard } = this.props; + const payload = { dashboard: dashboard.getSaveModelClone(), panelId }; + const testRuleResponse = await getBackendSrv().post(`/api/alerts/test`, payload); + this.setState(prevState => ({ ...prevState, isLoading: false, testRuleResponse })); + } + + render() { + const { testRuleResponse, isLoading } = this.state; + const { LoadingPlaceholder } = this.props; + + if (isLoading === true) { + return ; + } + + return ( + <> + + + ); + } +} From 8b8af6436c3b392dcf56b103db950c814251f743 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hugo=20H=C3=A4ggmark?= Date: Wed, 9 Jan 2019 11:38:10 +0100 Subject: [PATCH 23/91] Removed Test Rule button from Angular and view --- public/app/features/alerting/AlertTabCtrl.ts | 17 ----------------- .../features/alerting/partials/alert_tab.html | 14 -------------- 2 files changed, 31 deletions(-) diff --git a/public/app/features/alerting/AlertTabCtrl.ts b/public/app/features/alerting/AlertTabCtrl.ts index 2be25e9df6a..af00e79b085 100644 --- a/public/app/features/alerting/AlertTabCtrl.ts +++ b/public/app/features/alerting/AlertTabCtrl.ts @@ -9,8 +9,6 @@ import appEvents from 'app/core/app_events'; export class AlertTabCtrl { panel: any; panelCtrl: any; - testing: boolean; - testResult: any; subTabIndex: number; conditionTypes: any; alert: any; @@ -406,21 +404,6 @@ export class AlertTabCtrl { }, }); } - - test() { - this.testing = true; - this.testResult = false; - - const payload = { - dashboard: this.dashboardSrv.getCurrent().getSaveModelClone(), - panelId: this.panelCtrl.panel.id, - }; - - return this.backendSrv.post('/api/alerts/test', payload).then(res => { - this.testResult = res; - this.testing = false; - }); - } } /** @ngInject */ diff --git a/public/app/features/alerting/partials/alert_tab.html b/public/app/features/alerting/partials/alert_tab.html index da862203da6..90e0c7bbac2 100644 --- a/public/app/features/alerting/partials/alert_tab.html +++ b/public/app/features/alerting/partials/alert_tab.html @@ -121,20 +121,6 @@
- -
- -
- - -
- Evaluating rule -
- -
-
From 9b8a5333cb8cf3bcf41ec39c9f07d9270febad3e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hugo=20H=C3=A4ggmark?= Date: Wed, 9 Jan 2019 11:39:56 +0100 Subject: [PATCH 24/91] Added tests for TestRuleButton --- .../features/alerting/TestRuleButton.test.tsx | 44 +++++++++++++++++++ .../TestRuleButton.test.tsx.snap | 15 +++++++ 2 files changed, 59 insertions(+) create mode 100644 public/app/features/alerting/TestRuleButton.test.tsx create mode 100644 public/app/features/alerting/__snapshots__/TestRuleButton.test.tsx.snap diff --git a/public/app/features/alerting/TestRuleButton.test.tsx b/public/app/features/alerting/TestRuleButton.test.tsx new file mode 100644 index 00000000000..ae3b570cf43 --- /dev/null +++ b/public/app/features/alerting/TestRuleButton.test.tsx @@ -0,0 +1,44 @@ +import React from 'react'; +import { shallow } from 'enzyme'; +import { DashboardModel } from '../dashboard/dashboard_model'; +import { Props, TestRuleButton } from './TestRuleButton'; + +jest.mock('app/core/services/backend_srv', () => ({ + getBackendSrv: () => ({ + post: jest.fn(), + }), +})); + +const setup = (propOverrides?: object) => { + const props: Props = { + panelId: 1, + dashboard: new DashboardModel({ panels: [{ id: 1 }] }), + LoadingPlaceholder: {}, + }; + + Object.assign(props, propOverrides); + + const wrapper = shallow(); + + return { wrapper, instance: wrapper.instance() as TestRuleButton }; +}; + +describe('Render', () => { + it('should render component', () => { + const { wrapper } = setup(); + + expect(wrapper).toMatchSnapshot(); + }); +}); + +describe('Life cycle', () => { + describe('component did mount', () => { + it('should call testRule', () => { + const { instance } = setup(); + instance.testRule = jest.fn(); + instance.componentDidMount(); + + expect(instance.testRule).toHaveBeenCalled(); + }); + }); +}); diff --git a/public/app/features/alerting/__snapshots__/TestRuleButton.test.tsx.snap b/public/app/features/alerting/__snapshots__/TestRuleButton.test.tsx.snap new file mode 100644 index 00000000000..0e1c95d7233 --- /dev/null +++ b/public/app/features/alerting/__snapshots__/TestRuleButton.test.tsx.snap @@ -0,0 +1,15 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Render should render component 1`] = ` + + + +`; From 7fea1f84c0f5a206ba393e1406b35e82b2163a35 Mon Sep 17 00:00:00 2001 From: Leonard Gram Date: Fri, 14 Dec 2018 16:10:16 +0100 Subject: [PATCH 25/91] build: release of debs to our debian repo. --- .circleci/config.yml | 12 +++++++- scripts/build/load-signing-key.sh | 7 +++++ scripts/build/update_repo/aptly.conf | 27 ++++++++++++++++++ scripts/build/update_repo/sign-repo.sh | 7 +++++ scripts/build/update_repo/update-deb.sh | 38 +++++++++++++++++++++++++ scripts/build/update_repo/update-rpm.sh | 1 + 6 files changed, 91 insertions(+), 1 deletion(-) create mode 100644 scripts/build/load-signing-key.sh create mode 100644 scripts/build/update_repo/aptly.conf create mode 100755 scripts/build/update_repo/sign-repo.sh create mode 100755 scripts/build/update_repo/update-deb.sh create mode 100755 scripts/build/update_repo/update-rpm.sh diff --git a/.circleci/config.yml b/.circleci/config.yml index dba6c5f8bd0..1a1617ed407 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -398,8 +398,9 @@ jobs: deploy-release: docker: - - image: grafana/grafana-ci-deploy:1.0.0 + - image: xlson/aptly-ci:0.1 steps: + - checkout - attach_workspace: at: . - run: @@ -417,6 +418,15 @@ jobs: - run: name: Deploy to Grafana.com command: './scripts/build/publish.sh' + - run: + name: Load GPG private key + comand: './scripts/build/load-signing-key.sh' + - run: + name: Update Debian repository + command: './scripts/build/update_repo/update-deb.sh "oss" "$GPG_KEY_PASSWORD"' + - run: + name: Update RPM repository + command: 'ls' workflows: version: 2 diff --git a/scripts/build/load-signing-key.sh b/scripts/build/load-signing-key.sh new file mode 100644 index 00000000000..aa70d289443 --- /dev/null +++ b/scripts/build/load-signing-key.sh @@ -0,0 +1,7 @@ +#!/usr/bin/env bash + +set -e + +git clone git@github.com:torkelo/private.git ~/private-repo +gpg --batch --allow-secret-key-import --import ~/private-repo/signing/private.key +pkill gpg-agent \ No newline at end of file diff --git a/scripts/build/update_repo/aptly.conf b/scripts/build/update_repo/aptly.conf new file mode 100644 index 00000000000..5d2a64cd88d --- /dev/null +++ b/scripts/build/update_repo/aptly.conf @@ -0,0 +1,27 @@ +{ + "rootDir": "/deb-repo/db", + "downloadConcurrency": 4, + "downloadSpeedLimit": 0, + "architectures": [], + "dependencyFollowSuggests": false, + "dependencyFollowRecommends": false, + "dependencyFollowAllVariants": false, + "dependencyFollowSource": false, + "dependencyVerboseResolve": false, + "gpgDisableSign": false, + "gpgDisableVerify": false, + "gpgProvider": "gpg2", + "downloadSourcePackages": false, + "skipLegacyPool": true, + "ppaDistributorID": "ubuntu", + "ppaCodename": "", + "skipContentsPublishing": false, + "FileSystemPublishEndpoints": { + "repo": { + "rootDir": "/deb-repo/repo", + "linkMethod": "copy" + } + }, + "S3PublishEndpoints": {}, + "SwiftPublishEndpoints": {} +} diff --git a/scripts/build/update_repo/sign-repo.sh b/scripts/build/update_repo/sign-repo.sh new file mode 100755 index 00000000000..eb2922104fe --- /dev/null +++ b/scripts/build/update_repo/sign-repo.sh @@ -0,0 +1,7 @@ +#!/usr/bin/env expect + +set password [lindex $argv 0] +spawn aptly publish repo grafana filesystem:repo:grafana +expect "Enter passphrase: " +send -- "$password\r" +expect eof diff --git a/scripts/build/update_repo/update-deb.sh b/scripts/build/update_repo/update-deb.sh new file mode 100755 index 00000000000..08f5bd7ef6f --- /dev/null +++ b/scripts/build/update_repo/update-deb.sh @@ -0,0 +1,38 @@ +#!/usr/bin/env bash + +RELEASE_TYPE="${1:-}" +GPG_PASS="${2:-}" + +if [ -z "$RELEASE_TYPE" -o -z "$GPG_PASS" ]; then + exit 1 +fi + +set -e + +# Setup environment +cp scripts/build/update_repo/aptly.conf /etc/aptly.conf +mkdir -p /deb-repo/db +mkdir -p /deb-repo/repo + +# Download the database +gsutil -m rsync -r gs://grafana-aptly-db/repo-db /deb-repo/db + +# Add the new release to the repo +set +e +aptly publish drop squeeze filesystem:repo:grafana +set -e +aptly repo add grafana ./dist + +# Setup signing and sign the repo + +echo "allow-loopback-pinentry" > ~/.gnupg/gpg-agent.conf +echo "pinentry-mode loopback" > ~/.gnupg/gpg.conf + +./scripts/build/update_repo/sign-repo.sh "$GPG_PASS" + +# Update the repo and db on gcp +gsutil -m rsync -r -d /deb-repo/db gs://grafana-aptly-db/repo-db +gsutil -m rsync -r -d /deb-repo/repo/grafana "gs://grafana-repo/$RELEASE_TYPE/deb" + +# usage: +# deb https://grafana-repo.storage.googleapis.com/oss/deb squeeze main \ No newline at end of file diff --git a/scripts/build/update_repo/update-rpm.sh b/scripts/build/update_repo/update-rpm.sh new file mode 100755 index 00000000000..212c4ba239e --- /dev/null +++ b/scripts/build/update_repo/update-rpm.sh @@ -0,0 +1 @@ +#!/usr/bin/env bash \ No newline at end of file From a26a10cfd1ea2b0e571953aef1ca893746989cc9 Mon Sep 17 00:00:00 2001 From: Leonard Gram Date: Mon, 17 Dec 2018 11:09:35 +0100 Subject: [PATCH 26/91] build: repo update input error. --- scripts/build/update_repo/update-deb.sh | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/scripts/build/update_repo/update-deb.sh b/scripts/build/update_repo/update-deb.sh index 08f5bd7ef6f..d1694f0fee0 100755 --- a/scripts/build/update_repo/update-deb.sh +++ b/scripts/build/update_repo/update-deb.sh @@ -4,6 +4,7 @@ RELEASE_TYPE="${1:-}" GPG_PASS="${2:-}" if [ -z "$RELEASE_TYPE" -o -z "$GPG_PASS" ]; then + echo "Both RELEASE_TYPE (arg 1) and GPG_PASS (arg 2) has to be set" exit 1 fi @@ -35,4 +36,7 @@ gsutil -m rsync -r -d /deb-repo/db gs://grafana-aptly-db/repo-db gsutil -m rsync -r -d /deb-repo/repo/grafana "gs://grafana-repo/$RELEASE_TYPE/deb" # usage: -# deb https://grafana-repo.storage.googleapis.com/oss/deb squeeze main \ No newline at end of file +# deb https://grafana-repo.storage.googleapis.com/oss/deb squeeze main +# +# later: +# deb https://repo.grafana.com/oss/deb squeeze main From c3e23d7574face9ef72747faca1b1e92da9bc9d2 Mon Sep 17 00:00:00 2001 From: Leonard Gram Date: Mon, 17 Dec 2018 17:19:55 +0100 Subject: [PATCH 27/91] build: rpm repo deploy. --- .circleci/config.yml | 2 +- .../{sign-repo.sh => sign-deb-repo.sh} | 0 scripts/build/update_repo/sign-rpm-repo.sh | 7 +++ scripts/build/update_repo/update-deb.sh | 2 +- scripts/build/update_repo/update-rpm.sh | 44 ++++++++++++++++++- 5 files changed, 52 insertions(+), 3 deletions(-) rename scripts/build/update_repo/{sign-repo.sh => sign-deb-repo.sh} (100%) create mode 100755 scripts/build/update_repo/sign-rpm-repo.sh diff --git a/.circleci/config.yml b/.circleci/config.yml index 1a1617ed407..b5c123bad58 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -426,7 +426,7 @@ jobs: command: './scripts/build/update_repo/update-deb.sh "oss" "$GPG_KEY_PASSWORD"' - run: name: Update RPM repository - command: 'ls' + command: './scripts/build/update_repo/update-rpm.sh "oss" "$GPG_KEY_PASSWORD"' workflows: version: 2 diff --git a/scripts/build/update_repo/sign-repo.sh b/scripts/build/update_repo/sign-deb-repo.sh similarity index 100% rename from scripts/build/update_repo/sign-repo.sh rename to scripts/build/update_repo/sign-deb-repo.sh diff --git a/scripts/build/update_repo/sign-rpm-repo.sh b/scripts/build/update_repo/sign-rpm-repo.sh new file mode 100755 index 00000000000..f7e80756127 --- /dev/null +++ b/scripts/build/update_repo/sign-rpm-repo.sh @@ -0,0 +1,7 @@ +#!/usr/bin/env expect + +set password [lindex $argv 0] +spawn gpg --detach-sign --armor /rpm-repo/repodata/repomd.xml +expect "Enter passphrase: " +send -- "$password\r" +expect eof diff --git a/scripts/build/update_repo/update-deb.sh b/scripts/build/update_repo/update-deb.sh index d1694f0fee0..f2eb2f0dfd3 100755 --- a/scripts/build/update_repo/update-deb.sh +++ b/scripts/build/update_repo/update-deb.sh @@ -29,7 +29,7 @@ aptly repo add grafana ./dist echo "allow-loopback-pinentry" > ~/.gnupg/gpg-agent.conf echo "pinentry-mode loopback" > ~/.gnupg/gpg.conf -./scripts/build/update_repo/sign-repo.sh "$GPG_PASS" +./scripts/build/update_repo/sign-deb-repo.sh "$GPG_PASS" # Update the repo and db on gcp gsutil -m rsync -r -d /deb-repo/db gs://grafana-aptly-db/repo-db diff --git a/scripts/build/update_repo/update-rpm.sh b/scripts/build/update_repo/update-rpm.sh index 212c4ba239e..ca943957fe4 100755 --- a/scripts/build/update_repo/update-rpm.sh +++ b/scripts/build/update_repo/update-rpm.sh @@ -1 +1,43 @@ -#!/usr/bin/env bash \ No newline at end of file +#!/usr/bin/env bash + +RELEASE_TYPE="${1:-}" +GPG_PASS="${2:-}" + +if [ -z "$RELEASE_TYPE" -o -z "$GPG_PASS" ]; then + echo "Both RELEASE_TYPE (arg 1) and GPG_PASS (arg 2) has to be set" + exit 1 +fi + +set -e + +# Setup environment +mkdir -p /rpm-repo + +# Download the database +gsutil -m rsync -r "gs://grafana-repo/$RELEASE_TYPE/rpm" /rpm-repo + +# Add the new release to the repo +cp ./dist/*.rpm /rpm-repo +cd /rpm-repo +createrepo . + +# Setup signing and sign the repo + +echo "allow-loopback-pinentry" > ~/.gnupg/gpg-agent.conf +echo "pinentry-mode loopback" > ~/.gnupg/gpg.conf + +./scripts/build/update_repo/sign-rpm-repo.sh "$GPG_PASS" + +# Update the repo and db on gcp +gsutil -m rsync -r -d /rpm-repo "gs://grafana-repo/$RELEASE_TYPE/rpm" + +# usage: +# [grafana] +# name=grafana +# baseurl=https://grafana-repo.storage.googleapis.com/oss/rpm +# repo_gpgcheck=1 +# enabled=1 +# gpgcheck=1 +# gpgkey=https://grafana-repo.storage.googleapis.com/gpg.key https://grafanarel.s3.amazonaws.com/RPM-GPG-KEY-grafana +# sslverify=1 +# sslcacert=/etc/pki/tls/certs/ca-bundle.crt# later: From 919617ef963037c44c223c8e1ed8240c87392608 Mon Sep 17 00:00:00 2001 From: Leonard Gram Date: Tue, 18 Dec 2018 15:30:51 +0100 Subject: [PATCH 28/91] build: only adds the correct packages to the repo. --- scripts/build/update_repo/update-deb.sh | 7 ++++--- scripts/build/update_repo/update-rpm.sh | 1 + 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/scripts/build/update_repo/update-deb.sh b/scripts/build/update_repo/update-deb.sh index f2eb2f0dfd3..15c555d3426 100755 --- a/scripts/build/update_repo/update-deb.sh +++ b/scripts/build/update_repo/update-deb.sh @@ -14,14 +14,15 @@ set -e cp scripts/build/update_repo/aptly.conf /etc/aptly.conf mkdir -p /deb-repo/db mkdir -p /deb-repo/repo +mkdir -p /deb-repo/tmp # Download the database gsutil -m rsync -r gs://grafana-aptly-db/repo-db /deb-repo/db # Add the new release to the repo -set +e -aptly publish drop squeeze filesystem:repo:grafana -set -e +aptly publish drop squeeze filesystem:repo:grafana || true +cp ./dist/*.deb /deb-repo/tmp +rm /deb-repo/tmp/grafana_latest*.deb || true aptly repo add grafana ./dist # Setup signing and sign the repo diff --git a/scripts/build/update_repo/update-rpm.sh b/scripts/build/update_repo/update-rpm.sh index ca943957fe4..b89468938a6 100755 --- a/scripts/build/update_repo/update-rpm.sh +++ b/scripts/build/update_repo/update-rpm.sh @@ -18,6 +18,7 @@ gsutil -m rsync -r "gs://grafana-repo/$RELEASE_TYPE/rpm" /rpm-repo # Add the new release to the repo cp ./dist/*.rpm /rpm-repo +rm /rpm-repo/grafana-latest-1*.rpm || true cd /rpm-repo createrepo . From 31066aebb423ad5935d20449df85ef138ae1d772 Mon Sep 17 00:00:00 2001 From: Leonard Gram Date: Thu, 20 Dec 2018 11:11:20 +0100 Subject: [PATCH 29/91] build: handles unexpected cases. --- scripts/build/update_repo/update-deb.sh | 5 +++-- scripts/build/update_repo/update-rpm.sh | 2 ++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/scripts/build/update_repo/update-deb.sh b/scripts/build/update_repo/update-deb.sh index 15c555d3426..9184ed0369b 100755 --- a/scripts/build/update_repo/update-deb.sh +++ b/scripts/build/update_repo/update-deb.sh @@ -33,11 +33,12 @@ echo "pinentry-mode loopback" > ~/.gnupg/gpg.conf ./scripts/build/update_repo/sign-deb-repo.sh "$GPG_PASS" # Update the repo and db on gcp -gsutil -m rsync -r -d /deb-repo/db gs://grafana-aptly-db/repo-db +gsutil -m rsync -r -d /deb-repo/db gs://grafana-aptly-db/repo-db # TODO: support separate enterprise db gsutil -m rsync -r -d /deb-repo/repo/grafana "gs://grafana-repo/$RELEASE_TYPE/deb" # usage: # deb https://grafana-repo.storage.googleapis.com/oss/deb squeeze main # # later: -# deb https://repo.grafana.com/oss/deb squeeze main +# curl https://packages.grafana.com/gpg.key | apt-key add - +# deb https://packages.grafana.com/oss/deb squeeze main diff --git a/scripts/build/update_repo/update-rpm.sh b/scripts/build/update_repo/update-rpm.sh index b89468938a6..a98d00a108d 100755 --- a/scripts/build/update_repo/update-rpm.sh +++ b/scripts/build/update_repo/update-rpm.sh @@ -27,6 +27,8 @@ createrepo . echo "allow-loopback-pinentry" > ~/.gnupg/gpg-agent.conf echo "pinentry-mode loopback" > ~/.gnupg/gpg.conf +rm /rpm-repo/repodata/repomd.xml.asc || true +pkill gpg-agent || true ./scripts/build/update_repo/sign-rpm-repo.sh "$GPG_PASS" # Update the repo and db on gcp From a98c75121f7d30c5fda3ae8b6e0070501faf7e99 Mon Sep 17 00:00:00 2001 From: Leonard Gram Date: Fri, 21 Dec 2018 12:26:31 +0100 Subject: [PATCH 30/91] build: adds aptly and createrepo to deploy tools. --- scripts/build/ci-deploy/Dockerfile | 26 ++++++++++++++++++++++--- scripts/build/ci-deploy/build-deploy.sh | 2 +- 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/scripts/build/ci-deploy/Dockerfile b/scripts/build/ci-deploy/Dockerfile index deef612e761..f6683f9663c 100644 --- a/scripts/build/ci-deploy/Dockerfile +++ b/scripts/build/ci-deploy/Dockerfile @@ -1,5 +1,25 @@ +FROM circleci/golang:1.11 + +RUN git clone https://github.com/aptly-dev/aptly $GOPATH/src/github.com/aptly-dev/aptly && \ + cd $GOPATH/src/github.com/aptly-dev/aptly && \ + # pin aptly to a specific commit after 1.3.0 that contains gpg2 support + git reset --hard a64807efdaf5e380bfa878c71bc88eae10d62be1 && \ + make install + FROM circleci/python:2.7-stretch -RUN sudo pip install awscli && \ - curl https://dl.google.com/dl/cloudsdk/channels/rapid/downloads/google-cloud-sdk-222.0.0-linux-x86_64.tar.gz | \ - sudo tar xvzf - -C /opt +ENV PATH=$PATH:/opt/google-cloud-sdk/bin + +USER root + +RUN pip install awscli && \ + curl https://dl.google.com/dl/cloudsdk/channels/rapid/downloads/google-cloud-sdk-222.0.0-linux-x86_64.tar.gz | \ + tar xvzf - -C /opt && \ + apt update && \ + apt install -y createrepo expect && \ + apt-get autoremove -y && \ + rm -rf /var/lib/apt/lists/* + +COPY --from=0 /go/bin/aptly /usr/local/bin/aptly + +USER circleci diff --git a/scripts/build/ci-deploy/build-deploy.sh b/scripts/build/ci-deploy/build-deploy.sh index c9ce805b30b..818f91013ac 100755 --- a/scripts/build/ci-deploy/build-deploy.sh +++ b/scripts/build/ci-deploy/build-deploy.sh @@ -1,6 +1,6 @@ #!/bin/bash -_version="1.0.0" +_version="1.1.0" _tag="grafana/grafana-ci-deploy:${_version}" docker build -t $_tag . From 89956a6a41f1d31c7a57fe46cb9da65dbfe1f6e7 Mon Sep 17 00:00:00 2001 From: Leonard Gram Date: Fri, 21 Dec 2018 12:32:01 +0100 Subject: [PATCH 31/91] build: uses official deployment image. --- .circleci/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index b5c123bad58..8cbb124d7c3 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -398,7 +398,7 @@ jobs: deploy-release: docker: - - image: xlson/aptly-ci:0.1 + - image: grafana/grafana-ci-deploy:1.1.0 steps: - checkout - attach_workspace: From bbbeb78c17480c5f002c1f61cdb0bc4875495d96 Mon Sep 17 00:00:00 2001 From: Leonard Gram Date: Fri, 4 Jan 2019 10:18:44 +0100 Subject: [PATCH 32/91] build: makes repo update enterprise compatible. --- scripts/build/update_repo/update-deb.sh | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/scripts/build/update_repo/update-deb.sh b/scripts/build/update_repo/update-deb.sh index 9184ed0369b..92b0940d396 100755 --- a/scripts/build/update_repo/update-deb.sh +++ b/scripts/build/update_repo/update-deb.sh @@ -17,7 +17,7 @@ mkdir -p /deb-repo/repo mkdir -p /deb-repo/tmp # Download the database -gsutil -m rsync -r gs://grafana-aptly-db/repo-db /deb-repo/db +gsutil -m rsync -r "gs://grafana-aptly-db/$RELEASE_TYPE" /deb-repo/db # Add the new release to the repo aptly publish drop squeeze filesystem:repo:grafana || true @@ -33,12 +33,9 @@ echo "pinentry-mode loopback" > ~/.gnupg/gpg.conf ./scripts/build/update_repo/sign-deb-repo.sh "$GPG_PASS" # Update the repo and db on gcp -gsutil -m rsync -r -d /deb-repo/db gs://grafana-aptly-db/repo-db # TODO: support separate enterprise db +gsutil -m rsync -r -d /deb-repo/db "gs://grafana-aptly-db/$RELEASE_TYPE" gsutil -m rsync -r -d /deb-repo/repo/grafana "gs://grafana-repo/$RELEASE_TYPE/deb" # usage: -# deb https://grafana-repo.storage.googleapis.com/oss/deb squeeze main -# -# later: # curl https://packages.grafana.com/gpg.key | apt-key add - -# deb https://packages.grafana.com/oss/deb squeeze main +# deb https://packages.grafana.com/oss/deb stable main From 8f5886e6d444a2933c3d9912ed5cc0b8bd4b26f1 Mon Sep 17 00:00:00 2001 From: Leonard Gram Date: Fri, 4 Jan 2019 16:35:17 +0100 Subject: [PATCH 33/91] refactoring --- scripts/build/update_repo/update-deb.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/scripts/build/update_repo/update-deb.sh b/scripts/build/update_repo/update-deb.sh index 92b0940d396..bc6833d07f2 100755 --- a/scripts/build/update_repo/update-deb.sh +++ b/scripts/build/update_repo/update-deb.sh @@ -12,9 +12,9 @@ set -e # Setup environment cp scripts/build/update_repo/aptly.conf /etc/aptly.conf -mkdir -p /deb-repo/db -mkdir -p /deb-repo/repo -mkdir -p /deb-repo/tmp +mkdir -p /deb-repo/db \ + /deb-repo/repo \ + /deb-repo/tmp # Download the database gsutil -m rsync -r "gs://grafana-aptly-db/$RELEASE_TYPE" /deb-repo/db From b9c4eb70b13a4ee9a5eae150ee8ef02480ed2ad7 Mon Sep 17 00:00:00 2001 From: Leonard Gram Date: Tue, 8 Jan 2019 16:20:26 +0100 Subject: [PATCH 34/91] build: publishes beta releases to separate repos. --- .circleci/config.yml | 4 ++-- .../{sign-deb-repo.sh => unlock-gpg-key.sh} | 2 +- scripts/build/update_repo/update-deb.sh | 23 +++++++++++++++---- scripts/build/update_repo/update-rpm.sh | 17 ++++++++++++-- 4 files changed, 37 insertions(+), 9 deletions(-) rename scripts/build/update_repo/{sign-deb-repo.sh => unlock-gpg-key.sh} (66%) diff --git a/.circleci/config.yml b/.circleci/config.yml index 8cbb124d7c3..58357c1d490 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -423,10 +423,10 @@ jobs: comand: './scripts/build/load-signing-key.sh' - run: name: Update Debian repository - command: './scripts/build/update_repo/update-deb.sh "oss" "$GPG_KEY_PASSWORD"' + command: './scripts/build/update_repo/update-deb.sh "oss" "$GPG_KEY_PASSWORD" "$CIRCLE_TAG"' - run: name: Update RPM repository - command: './scripts/build/update_repo/update-rpm.sh "oss" "$GPG_KEY_PASSWORD"' + command: './scripts/build/update_repo/update-rpm.sh "oss" "$GPG_KEY_PASSWORD" "$CIRCLE_TAG"' workflows: version: 2 diff --git a/scripts/build/update_repo/sign-deb-repo.sh b/scripts/build/update_repo/unlock-gpg-key.sh similarity index 66% rename from scripts/build/update_repo/sign-deb-repo.sh rename to scripts/build/update_repo/unlock-gpg-key.sh index eb2922104fe..82f981809c2 100755 --- a/scripts/build/update_repo/sign-deb-repo.sh +++ b/scripts/build/update_repo/unlock-gpg-key.sh @@ -1,7 +1,7 @@ #!/usr/bin/env expect set password [lindex $argv 0] -spawn aptly publish repo grafana filesystem:repo:grafana +spawn gpg --detach-sign --armor /tmp/sign-this expect "Enter passphrase: " send -- "$password\r" expect eof diff --git a/scripts/build/update_repo/update-deb.sh b/scripts/build/update_repo/update-deb.sh index bc6833d07f2..b08ff36149f 100755 --- a/scripts/build/update_repo/update-deb.sh +++ b/scripts/build/update_repo/update-deb.sh @@ -2,12 +2,23 @@ RELEASE_TYPE="${1:-}" GPG_PASS="${2:-}" +RELEASE_TAG="${3:-}" +REPO="grafana" if [ -z "$RELEASE_TYPE" -o -z "$GPG_PASS" ]; then echo "Both RELEASE_TYPE (arg 1) and GPG_PASS (arg 2) has to be set" exit 1 fi +if [[ "$RELEASE_TYPE" != "oss" && "$RELEASE_TYPE" != "enterprise" ]]; then + echo "RELEASE_TYPE (arg 1) must be either oss or enterprise." + exit 1 +fi + +if echo "$RELEASE_TAG" | grep -q "beta"; then + REPO="beta" +fi + set -e # Setup environment @@ -20,22 +31,26 @@ mkdir -p /deb-repo/db \ gsutil -m rsync -r "gs://grafana-aptly-db/$RELEASE_TYPE" /deb-repo/db # Add the new release to the repo -aptly publish drop squeeze filesystem:repo:grafana || true +aptly publish drop grafana filesystem:repo:grafana || true +aptly publish drop beta filesystem:repo:grafana || true cp ./dist/*.deb /deb-repo/tmp rm /deb-repo/tmp/grafana_latest*.deb || true -aptly repo add grafana ./dist +aptly repo add "$REPO" ./dist # Setup signing and sign the repo echo "allow-loopback-pinentry" > ~/.gnupg/gpg-agent.conf echo "pinentry-mode loopback" > ~/.gnupg/gpg.conf -./scripts/build/update_repo/sign-deb-repo.sh "$GPG_PASS" +./scripts/build/update_repo/unlock-gpg-key.sh "$GPG_PASS" + +aptly publish repo grafana filesystem:repo:grafana +aptly publish repo beta filesystem:repo:grafana # Update the repo and db on gcp gsutil -m rsync -r -d /deb-repo/db "gs://grafana-aptly-db/$RELEASE_TYPE" gsutil -m rsync -r -d /deb-repo/repo/grafana "gs://grafana-repo/$RELEASE_TYPE/deb" # usage: -# curl https://packages.grafana.com/gpg.key | apt-key add - +# # deb https://packages.grafana.com/oss/deb stable main diff --git a/scripts/build/update_repo/update-rpm.sh b/scripts/build/update_repo/update-rpm.sh index a98d00a108d..26eb2c5b329 100755 --- a/scripts/build/update_repo/update-rpm.sh +++ b/scripts/build/update_repo/update-rpm.sh @@ -3,18 +3,31 @@ RELEASE_TYPE="${1:-}" GPG_PASS="${2:-}" +RELEASE_TAG="${3:-}" +REPO="rpm" + if [ -z "$RELEASE_TYPE" -o -z "$GPG_PASS" ]; then echo "Both RELEASE_TYPE (arg 1) and GPG_PASS (arg 2) has to be set" exit 1 fi +if [[ "$RELEASE_TYPE" != "oss" && "$RELEASE_TYPE" != "enterprise" ]]; then + echo "RELEASE_TYPE (arg 1) must be either oss or enterprise." + exit 1 +fi + +if echo "$RELEASE_TAG" | grep -q "beta"; then + REPO="rpm-beta" +fi + set -e # Setup environment +BUCKET="gs://grafana-repo/$RELEASE_TYPE/$REPO" mkdir -p /rpm-repo # Download the database -gsutil -m rsync -r "gs://grafana-repo/$RELEASE_TYPE/rpm" /rpm-repo +gsutil -m rsync -r "$BUCKET" /rpm-repo # Add the new release to the repo cp ./dist/*.rpm /rpm-repo @@ -32,7 +45,7 @@ pkill gpg-agent || true ./scripts/build/update_repo/sign-rpm-repo.sh "$GPG_PASS" # Update the repo and db on gcp -gsutil -m rsync -r -d /rpm-repo "gs://grafana-repo/$RELEASE_TYPE/rpm" +gsutil -m rsync -r -d /rpm-repo "$BUCKET" # usage: # [grafana] From 76e9607b25bfde8562c66550430474544339bb6c Mon Sep 17 00:00:00 2001 From: Leonard Gram Date: Tue, 8 Jan 2019 16:34:59 +0100 Subject: [PATCH 35/91] build: inline docs --- scripts/build/update_repo/update-rpm.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/scripts/build/update_repo/update-rpm.sh b/scripts/build/update_repo/update-rpm.sh index 26eb2c5b329..caed3918216 100755 --- a/scripts/build/update_repo/update-rpm.sh +++ b/scripts/build/update_repo/update-rpm.sh @@ -50,10 +50,10 @@ gsutil -m rsync -r -d /rpm-repo "$BUCKET" # usage: # [grafana] # name=grafana -# baseurl=https://grafana-repo.storage.googleapis.com/oss/rpm +# baseurl=https://packages.grafana.com/oss/rpm # repo_gpgcheck=1 # enabled=1 # gpgcheck=1 -# gpgkey=https://grafana-repo.storage.googleapis.com/gpg.key https://grafanarel.s3.amazonaws.com/RPM-GPG-KEY-grafana +# gpgkey=https://packages.grafana.com/gpg.key # sslverify=1 -# sslcacert=/etc/pki/tls/certs/ca-bundle.crt# later: +# sslcacert=/etc/pki/tls/certs/ca-bundle.crt \ No newline at end of file From ad61bff3779b9079baeb01d56c00f51f73a6bcda Mon Sep 17 00:00:00 2001 From: Leonard Gram Date: Wed, 9 Jan 2019 11:17:21 +0100 Subject: [PATCH 36/91] build: deploys enterprise to its own repo. --- .circleci/config.yml | 16 +++++++++++++--- scripts/build/update_repo/update-deb.sh | 2 ++ 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 58357c1d490..236d5aec398 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -323,7 +323,7 @@ jobs: deploy-enterprise-master: docker: - - image: grafana/grafana-ci-deploy:1.0.0 + - image: grafana/grafana-ci-deploy:1.1.0 steps: - attach_workspace: at: . @@ -346,7 +346,7 @@ jobs: deploy-enterprise-release: docker: - - image: grafana/grafana-ci-deploy:1.0.0 + - image: grafana/grafana-ci-deploy:1.1.0 steps: - attach_workspace: at: . @@ -365,10 +365,20 @@ jobs: - run: name: Deploy to Grafana.com command: './scripts/build/publish.sh --enterprise' + - run: + name: Load GPG private key + comand: './scripts/build/load-signing-key.sh' + - run: + name: Update Debian repository + command: './scripts/build/update_repo/update-deb.sh "enterprise" "$GPG_KEY_PASSWORD" "$CIRCLE_TAG"' + - run: + name: Update RPM repository + command: './scripts/build/update_repo/update-rpm.sh "enterprise" "$GPG_KEY_PASSWORD" "$CIRCLE_TAG"' + deploy-master: docker: - - image: grafana/grafana-ci-deploy:1.0.0 + - image: grafana/grafana-ci-deploy:1.1.0 steps: - attach_workspace: at: . diff --git a/scripts/build/update_repo/update-deb.sh b/scripts/build/update_repo/update-deb.sh index b08ff36149f..89c5937b064 100755 --- a/scripts/build/update_repo/update-deb.sh +++ b/scripts/build/update_repo/update-deb.sh @@ -42,7 +42,9 @@ aptly repo add "$REPO" ./dist echo "allow-loopback-pinentry" > ~/.gnupg/gpg-agent.conf echo "pinentry-mode loopback" > ~/.gnupg/gpg.conf +touch /tmp/sign-this ./scripts/build/update_repo/unlock-gpg-key.sh "$GPG_PASS" +rm /tmp/sign-this /tmp/sign-this.asc aptly publish repo grafana filesystem:repo:grafana aptly publish repo beta filesystem:repo:grafana From 67e8958aec04fed841c8ac0b7f3a6bb0bc4e4af3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hugo=20H=C3=A4ggmark?= Date: Wed, 9 Jan 2019 11:49:17 +0100 Subject: [PATCH 37/91] Fixed a small bug when toggling items in toolbar --- public/app/features/dashboard/dashgrid/EditorTabBody.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/app/features/dashboard/dashgrid/EditorTabBody.tsx b/public/app/features/dashboard/dashgrid/EditorTabBody.tsx index b7da81a23f8..95397b884c8 100644 --- a/public/app/features/dashboard/dashgrid/EditorTabBody.tsx +++ b/public/app/features/dashboard/dashgrid/EditorTabBody.tsx @@ -52,7 +52,7 @@ export class EditorTabBody extends PureComponent { onToggleToolBarView = (item: EditorToolbarView) => { this.setState({ openView: item, - isOpen: !this.state.isOpen, + isOpen: this.state.openView !== item ? true : !this.state.isOpen, }); }; From 6e3225c29e399ce4d0837975773a2acb93cd61d7 Mon Sep 17 00:00:00 2001 From: Dominik Prokop Date: Wed, 9 Jan 2019 13:09:26 +0100 Subject: [PATCH 38/91] Removed unused refClassNameprops from Propper --- packages/grafana-ui/src/components/Tooltip/Popper.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/grafana-ui/src/components/Tooltip/Popper.tsx b/packages/grafana-ui/src/components/Tooltip/Popper.tsx index b405d4c4328..f6f9fa6f73a 100644 --- a/packages/grafana-ui/src/components/Tooltip/Popper.tsx +++ b/packages/grafana-ui/src/components/Tooltip/Popper.tsx @@ -26,7 +26,6 @@ interface Props extends React.DOMAttributes { show: boolean; placement?: PopperJS.Placement; content: string | ((props: any) => JSX.Element); - refClassName?: string; referenceElement: PopperJS.ReferenceObject; theme?: Themes; } From 236d7b12138928eee17560cb3071e2414651f3bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hugo=20H=C3=A4ggmark?= Date: Wed, 9 Jan 2019 13:20:50 +0100 Subject: [PATCH 39/91] Changes after PR comments --- .../app/features/alerting/TestRuleButton.tsx | 14 +++++--------- .../__snapshots__/TestRuleButton.test.tsx.snap | 18 ++++++++---------- .../dashboard/dashgrid/EditorTabBody.tsx | 2 +- 3 files changed, 14 insertions(+), 20 deletions(-) diff --git a/public/app/features/alerting/TestRuleButton.tsx b/public/app/features/alerting/TestRuleButton.tsx index 032d7c7e4f8..f9927b1a182 100644 --- a/public/app/features/alerting/TestRuleButton.tsx +++ b/public/app/features/alerting/TestRuleButton.tsx @@ -15,10 +15,10 @@ interface State { } export class TestRuleButton extends PureComponent { - constructor(props) { - super(props); - this.state = { isLoading: false, testRuleResponse: {} }; - } + readonly state: State = { + isLoading: false, + testRuleResponse: {}, + }; componentDidMount() { this.testRule(); @@ -39,10 +39,6 @@ export class TestRuleButton extends PureComponent { return ; } - return ( - <> - - - ); + return ; } } diff --git a/public/app/features/alerting/__snapshots__/TestRuleButton.test.tsx.snap b/public/app/features/alerting/__snapshots__/TestRuleButton.test.tsx.snap index 0e1c95d7233..d1ed3e64e99 100644 --- a/public/app/features/alerting/__snapshots__/TestRuleButton.test.tsx.snap +++ b/public/app/features/alerting/__snapshots__/TestRuleButton.test.tsx.snap @@ -1,15 +1,13 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`Render should render component 1`] = ` - - - + } + json={Object {}} + open={3} +/> `; diff --git a/public/app/features/dashboard/dashgrid/EditorTabBody.tsx b/public/app/features/dashboard/dashgrid/EditorTabBody.tsx index 95397b884c8..ebf57613699 100644 --- a/public/app/features/dashboard/dashgrid/EditorTabBody.tsx +++ b/public/app/features/dashboard/dashgrid/EditorTabBody.tsx @@ -52,7 +52,7 @@ export class EditorTabBody extends PureComponent { onToggleToolBarView = (item: EditorToolbarView) => { this.setState({ openView: item, - isOpen: this.state.openView !== item ? true : !this.state.isOpen, + isOpen: this.state.openView !== item || !this.state.isOpen, }); }; From 6ab1abc131150ecb0aa615eed75711676fc43bd8 Mon Sep 17 00:00:00 2001 From: Johannes Schill Date: Wed, 9 Jan 2019 14:02:22 +0100 Subject: [PATCH 40/91] chore: Remove ScrollBar component, superseded by CustomScrollbar --- .../core/components/ScrollBar/ScrollBar.tsx | 78 ------------------- 1 file changed, 78 deletions(-) delete mode 100644 public/app/core/components/ScrollBar/ScrollBar.tsx diff --git a/public/app/core/components/ScrollBar/ScrollBar.tsx b/public/app/core/components/ScrollBar/ScrollBar.tsx deleted file mode 100644 index 24d17f67367..00000000000 --- a/public/app/core/components/ScrollBar/ScrollBar.tsx +++ /dev/null @@ -1,78 +0,0 @@ -import React from 'react'; -import baron from 'baron'; - -export interface Props { - children: any; - className: string; -} - -export default class ScrollBar extends React.Component { - private container: any; - private scrollbar: baron; - - constructor(props) { - super(props); - } - - componentDidMount() { - this.scrollbar = baron({ - root: this.container.parentElement, - scroller: this.container, - bar: '.baron__bar', - barOnCls: '_scrollbar', - scrollingCls: '_scrolling', - track: '.baron__track', - }); - } - - componentDidUpdate() { - this.scrollbar.update(); - } - - componentWillUnmount() { - this.scrollbar.dispose(); - } - - // methods can be invoked by outside - setScrollTop(top) { - if (this.container) { - this.container.scrollTop = top; - this.scrollbar.update(); - - return true; - } - return false; - } - - setScrollLeft(left) { - if (this.container) { - this.container.scrollLeft = left; - this.scrollbar.update(); - - return true; - } - return false; - } - - update() { - this.scrollbar.update(); - } - - handleRef = ref => { - this.container = ref; - }; - - render() { - return ( -
-
- {this.props.children} -
- -
-
-
-
- ); - } -} From 5f7e6a5c7378ebf4fa534e3b3de8b7f293c1f0a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20W=C3=BCrbach?= Date: Thu, 20 Dec 2018 22:48:53 +0100 Subject: [PATCH 41/91] Docker image for ARM --- .circleci/config.yml | 44 +++++++++++++------------- build.go | 2 ++ packaging/docker/Dockerfile | 6 ++-- packaging/docker/build.sh | 34 +++++++++++++++++--- packaging/docker/push_to_docker_hub.sh | 29 ++++++++++++++--- scripts/build/build.sh | 7 ++++ 6 files changed, 89 insertions(+), 33 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 236d5aec398..3d66a8ef13b 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -200,47 +200,47 @@ jobs: - dist/grafana* grafana-docker-master: - docker: - - image: docker:stable-git + machine: + image: circleci/classic:201808-01 steps: - checkout - attach_workspace: at: . - - setup_remote_docker - run: docker info - - run: cp dist/grafana-latest.linux-x64.tar.gz packaging/docker + - run: docker run --privileged linuxkit/binfmt:v0.6 + - run: cp dist/grafana-latest.linux-*.tar.gz packaging/docker - run: cd packaging/docker && ./build-deploy.sh "master-${CIRCLE_SHA1}" - - run: rm packaging/docker/grafana-latest.linux-x64.tar.gz + - run: rm packaging/docker/grafana-latest.linux-*.tar.gz - run: cp enterprise-dist/grafana-enterprise-*.linux-amd64.tar.gz packaging/docker/grafana-latest.linux-x64.tar.gz - run: cd packaging/docker && ./build-enterprise.sh "master" grafana-docker-pr: - docker: - - image: docker:stable-git + machine: + image: circleci/classic:201808-01 steps: - checkout - attach_workspace: at: . - - setup_remote_docker - run: docker info - - run: cp dist/grafana-latest.linux-x64.tar.gz packaging/docker + - run: docker run --privileged linuxkit/binfmt:v0.6 + - run: cp dist/grafana-latest.linux-*.tar.gz packaging/docker - run: cd packaging/docker && ./build.sh "${CIRCLE_SHA1}" grafana-docker-release: - docker: - - image: docker:stable-git - steps: - - checkout - - attach_workspace: - at: . - - setup_remote_docker - - run: docker info - - run: cp dist/grafana-latest.linux-x64.tar.gz packaging/docker - - run: cd packaging/docker && ./build-deploy.sh "${CIRCLE_TAG}" - - run: rm packaging/docker/grafana-latest.linux-x64.tar.gz - - run: cp enterprise-dist/grafana-enterprise-*.linux-amd64.tar.gz packaging/docker/grafana-latest.linux-x64.tar.gz - - run: cd packaging/docker && ./build-enterprise.sh "${CIRCLE_TAG}" + machine: + image: circleci/classic:201808-01 + steps: + - checkout + - attach_workspace: + at: . + - run: docker info + - run: docker run --privileged linuxkit/binfmt:v0.6 + - run: cp dist/grafana-latest.linux-*.tar.gz packaging/docker + - run: cd packaging/docker && ./build-deploy.sh "${CIRCLE_TAG}" + - run: rm packaging/docker/grafana-latest.linux-*.tar.gz + - run: cp enterprise-dist/grafana-enterprise-*.linux-amd64.tar.gz packaging/docker/grafana-latest.linux-x64.tar.gz + - run: cd packaging/docker && ./build-enterprise.sh "${CIRCLE_TAG}" build-enterprise: docker: diff --git a/build.go b/build.go index 9d5216de1d0..4486cd3deb9 100644 --- a/build.go +++ b/build.go @@ -164,6 +164,8 @@ func makeLatestDistCopies() { "_amd64.deb": "dist/grafana_latest_amd64.deb", ".x86_64.rpm": "dist/grafana-latest-1.x86_64.rpm", ".linux-amd64.tar.gz": "dist/grafana-latest.linux-x64.tar.gz", + ".linux-armv7.tar.gz": "dist/grafana-latest.linux-armv7.tar.gz", + ".linux-arm64.tar.gz": "dist/grafana-latest.linux-arm64.tar.gz", } for _, file := range files { diff --git a/packaging/docker/Dockerfile b/packaging/docker/Dockerfile index 4d4f6539972..d4f2f2aa7a3 100644 --- a/packaging/docker/Dockerfile +++ b/packaging/docker/Dockerfile @@ -1,4 +1,5 @@ -FROM debian:stretch-slim +ARG BASE_IMAGE=debian:stretch-slim +FROM ${BASE_IMAGE} ARG GRAFANA_TGZ="grafana-latest.linux-x64.tar.gz" @@ -10,7 +11,8 @@ COPY ${GRAFANA_TGZ} /tmp/grafana.tar.gz RUN mkdir /tmp/grafana && tar xfvz /tmp/grafana.tar.gz --strip-components=1 -C /tmp/grafana -FROM debian:stretch-slim +ARG BASE_IMAGE=debian:stretch-slim +FROM ${BASE_IMAGE} ARG GF_UID="472" ARG GF_GID="472" diff --git a/packaging/docker/build.sh b/packaging/docker/build.sh index c303c71cd5f..860df4780b5 100755 --- a/packaging/docker/build.sh +++ b/packaging/docker/build.sh @@ -13,13 +13,37 @@ fi echo "Building ${_docker_repo}:${_grafana_version}" -docker build \ - --tag "${_docker_repo}:${_grafana_version}" \ - --no-cache=true . +export DOCKER_CLI_EXPERIMENTAL=enabled + +# Build grafana image for a specific arch +docker_build () { + base_image=$1 + grafana_tgz=$2 + tag=$3 + + docker build \ + --build-arg BASE_IMAGE=${base_image} \ + --build-arg GRAFANA_TGZ=${grafana_tgz} \ + --tag "${tag}" \ + --no-cache=true . +} + +# Tag docker images of all architectures +docker_tag_all () { + repo=$1 + tag=$2 + docker tag "${_docker_repo}:${_grafana_version}" "${repo}:${tag}" + docker tag "${_docker_repo}-arm32v7-linux:${_grafana_version}" "${repo}-arm32v7-linux:${tag}" + docker tag "${_docker_repo}-arm64v8-linux:${_grafana_version}" "${repo}-arm64v8-linux:${tag}" +} + +docker_build "debian:stretch-slim" "grafana-latest.linux-x64.tar.gz" "${_docker_repo}:${_grafana_version}" +docker_build "arm32v7/debian:stretch-slim" "grafana-latest.linux-armv7.tar.gz" "${_docker_repo}-arm32v7-linux:${_grafana_version}" +docker_build "arm64v8/debian:stretch-slim" "grafana-latest.linux-arm64.tar.gz" "${_docker_repo}-arm64v8-linux:${_grafana_version}" # Tag as 'latest' for official release; otherwise tag as grafana/grafana:master if echo "$_grafana_tag" | grep -q "^v"; then - docker tag "${_docker_repo}:${_grafana_version}" "${_docker_repo}:latest" + docker_tag_all "${_docker_repo}" "latest" else - docker tag "${_docker_repo}:${_grafana_version}" "grafana/grafana:master" + docker_tag_all "grafana/grafana" "master" fi diff --git a/packaging/docker/push_to_docker_hub.sh b/packaging/docker/push_to_docker_hub.sh index 526c216f8fa..b873dbe05e0 100755 --- a/packaging/docker/push_to_docker_hub.sh +++ b/packaging/docker/push_to_docker_hub.sh @@ -12,13 +12,34 @@ else _docker_repo=${2:-grafana/grafana-dev} fi +export DOCKER_CLI_EXPERIMENTAL=enabled + echo "pushing ${_docker_repo}:${_grafana_version}" -docker push "${_docker_repo}:${_grafana_version}" + + +docker_push_all () { + repo=$1 + tag=$2 + + # Push each image individually + docker push "${repo}:${tag}" + docker push "${repo}-arm32v7-linux:${tag}" + docker push "${repo}-arm64v8-linux:${tag}" + + # Create and push a multi-arch manifest + docker manifest create "${repo}:${tag}" \ + "${repo}:${tag}" \ + "${repo}-arm32v7-linux:${tag}" \ + "${repo}-arm64v8-linux:${tag}" + + docker manifest push "${repo}:${tag}" +} + +docker_push_all "${_docker_repo}" "${_grafana_version}" if echo "$_grafana_tag" | grep -q "^v" && echo "$_grafana_tag" | grep -vq "beta"; then echo "pushing ${_docker_repo}:latest" - docker push "${_docker_repo}:latest" + docker_push_all "${_docker_repo}" "latest" elif echo "$_grafana_tag" | grep -q "master"; then - echo "pushing grafana/grafana:master" - docker push grafana/grafana:master + docker_push_all "grafana/grafana" "master" fi diff --git a/scripts/build/build.sh b/scripts/build/build.sh index 8362942c6cd..1222053f1c8 100755 --- a/scripts/build/build.sh +++ b/scripts/build/build.sh @@ -8,6 +8,8 @@ set -e EXTRA_OPTS="$@" +CCARMV7=arm-linux-gnueabihf-gcc +CCARM64=aarch64-linux-gnu-gcc CCX64=/tmp/x86_64-centos6-linux-gnu/bin/x86_64-centos6-linux-gnu-gcc GOPATH=/go @@ -26,6 +28,9 @@ fi echo "Build arguments: $OPT" +go run build.go -goarch armv7 -cc ${CCARMV7} ${OPT} build +go run build.go -goarch arm64 -cc ${CCARM64} ${OPT} build + CC=${CCX64} go run build.go ${OPT} build yarn install --pure-lockfile --no-progress @@ -44,3 +49,5 @@ source /etc/profile.d/rvm.sh echo "Packaging" go run build.go -goos linux -pkg-arch amd64 ${OPT} package-only latest +go run build.go -goos linux -pkg-arch armv7 ${OPT} package-only latest +go run build.go -goos linux -pkg-arch arm64 ${OPT} package-only latest From d8a91fa3557a0e8d22f6e3b0833d7a115e8049bc Mon Sep 17 00:00:00 2001 From: Johannes Schill Date: Wed, 9 Jan 2019 15:36:53 +0100 Subject: [PATCH 42/91] feat: Add brand as tooltip theme and use it on panel edit tabs #14271 --- .../src/components/Tooltip/Popper.tsx | 1 + .../src/components/Tooltip/_Tooltip.scss | 18 ++++++++++++++---- .../dashboard/dashgrid/PanelEditor.tsx | 3 ++- public/sass/_variables.dark.scss | 1 + public/sass/_variables.light.scss | 1 + 5 files changed, 19 insertions(+), 5 deletions(-) diff --git a/packages/grafana-ui/src/components/Tooltip/Popper.tsx b/packages/grafana-ui/src/components/Tooltip/Popper.tsx index f6f9fa6f73a..eb64df1cb6e 100644 --- a/packages/grafana-ui/src/components/Tooltip/Popper.tsx +++ b/packages/grafana-ui/src/components/Tooltip/Popper.tsx @@ -7,6 +7,7 @@ import Transition from 'react-transition-group/Transition'; export enum Themes { Default = 'popper__background--default', Error = 'popper__background--error', + Brand = 'popper__background--brand', } const defaultTransitionStyles = { diff --git a/packages/grafana-ui/src/components/Tooltip/_Tooltip.scss b/packages/grafana-ui/src/components/Tooltip/_Tooltip.scss index afa629d4043..c8fa099cce6 100644 --- a/packages/grafana-ui/src/components/Tooltip/_Tooltip.scss +++ b/packages/grafana-ui/src/components/Tooltip/_Tooltip.scss @@ -1,5 +1,13 @@ $popper-margin-from-ref: 5px; + +@mixin popper-theme($backgroundColor, $arrowColor) { + background: $backgroundColor; + .popper__arrow { + border-color: $arrowColor; + } +} + .popper { position: absolute; z-index: $zindex-tooltip; @@ -16,10 +24,12 @@ $popper-margin-from-ref: 5px; // Themes &.popper__background--error { - background: $tooltipBackgroundError; - .popper__arrow { - border-color: $tooltipBackgroundError; - } + @include popper-theme($tooltipBackgroundError, $tooltipBackgroundError); + } + + &.popper__background--brand { + @include popper-theme($tooltipBackgroundBrand, $tooltipBackgroundBrand); + @include gradient-vertical($red, $orange); } } diff --git a/public/app/features/dashboard/dashgrid/PanelEditor.tsx b/public/app/features/dashboard/dashgrid/PanelEditor.tsx index 123204aa239..a09ff66f114 100644 --- a/public/app/features/dashboard/dashgrid/PanelEditor.tsx +++ b/public/app/features/dashboard/dashgrid/PanelEditor.tsx @@ -16,6 +16,7 @@ import { DashboardModel } from '../dashboard_model'; import { PanelPlugin } from 'app/types/plugins'; import { Tooltip } from '@grafana/ui'; +import { Themes } from '@grafana/ui/src/components/Tooltip/Popper'; interface PanelEditorProps { panel: PanelModel; @@ -138,7 +139,7 @@ function TabItem({ tab, activeTab, onClick }: TabItemParams) { return (
onClick(tab)}> - + diff --git a/public/sass/_variables.dark.scss b/public/sass/_variables.dark.scss index ded17e6ecfe..5640ff1775e 100644 --- a/public/sass/_variables.dark.scss +++ b/public/sass/_variables.dark.scss @@ -310,6 +310,7 @@ $graph-tooltip-bg: $dark-1; $tooltipBackground: $popover-help-bg; $tooltipArrowColor: $tooltipBackground; $tooltipBackgroundError: $brand-danger; +$tooltipBackgroundBrand: $brand-primary; // images $checkboxImageUrl: '../img/checkbox.png'; diff --git a/public/sass/_variables.light.scss b/public/sass/_variables.light.scss index ec25be52676..be8df389c1b 100644 --- a/public/sass/_variables.light.scss +++ b/public/sass/_variables.light.scss @@ -314,6 +314,7 @@ $graph-tooltip-bg: $gray-5; $tooltipBackground: $popover-help-bg; $tooltipArrowColor: $tooltipBackground; // Used by Angular tooltip $tooltipBackgroundError: $brand-danger; +$tooltipBackgroundBrand: $brand-primary; // images $checkboxImageUrl: '../img/checkbox_white.png'; From be57f6878cd094893005a179f66de19f17fe9fd5 Mon Sep 17 00:00:00 2001 From: Leonard Gram Date: Wed, 9 Jan 2019 15:50:52 +0100 Subject: [PATCH 43/91] build: fixes docker push. --- packaging/docker/build.sh | 8 ++++---- packaging/docker/push_to_docker_hub.sh | 11 ++++++----- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/packaging/docker/build.sh b/packaging/docker/build.sh index 860df4780b5..1bad2980d34 100755 --- a/packaging/docker/build.sh +++ b/packaging/docker/build.sh @@ -1,14 +1,13 @@ #!/bin/sh -_grafana_tag=$1 +_grafana_tag=${1:-} +_docker_repo=${2:-grafana/grafana} # If the tag starts with v, treat this as a official release if echo "$_grafana_tag" | grep -q "^v"; then _grafana_version=$(echo "${_grafana_tag}" | cut -d "v" -f 2) - _docker_repo=${2:-grafana/grafana} else _grafana_version=$_grafana_tag - _docker_repo=${2:-grafana/grafana-dev} fi echo "Building ${_docker_repo}:${_grafana_version}" @@ -45,5 +44,6 @@ docker_build "arm64v8/debian:stretch-slim" "grafana-latest.linux-arm64.tar.gz" " if echo "$_grafana_tag" | grep -q "^v"; then docker_tag_all "${_docker_repo}" "latest" else - docker_tag_all "grafana/grafana" "master" + docker_tag_all "${_docker_repo}" "master" + docker tag "${_docker_repo}:${_grafana_version} grafana/grafana-dev:${_grafana_version}" fi diff --git a/packaging/docker/push_to_docker_hub.sh b/packaging/docker/push_to_docker_hub.sh index b873dbe05e0..cef6d596851 100755 --- a/packaging/docker/push_to_docker_hub.sh +++ b/packaging/docker/push_to_docker_hub.sh @@ -1,15 +1,14 @@ #!/bin/sh set -e -_grafana_tag=$1 +_grafana_tag=${1:-} +_docker_repo=${2:-grafana/grafana} # If the tag starts with v, treat this as a official release if echo "$_grafana_tag" | grep -q "^v"; then _grafana_version=$(echo "${_grafana_tag}" | cut -d "v" -f 2) - _docker_repo=${2:-grafana/grafana} else _grafana_version=$_grafana_tag - _docker_repo=${2:-grafana/grafana-dev} fi export DOCKER_CLI_EXPERIMENTAL=enabled @@ -35,11 +34,13 @@ docker_push_all () { docker manifest push "${repo}:${tag}" } -docker_push_all "${_docker_repo}" "${_grafana_version}" - if echo "$_grafana_tag" | grep -q "^v" && echo "$_grafana_tag" | grep -vq "beta"; then echo "pushing ${_docker_repo}:latest" docker_push_all "${_docker_repo}" "latest" + docker_push_all "${_docker_repo}" "${_grafana_version}" +elif echo "$_grafana_tag" | grep -q "^v" && echo "$_grafana_tag" | grep -q "beta"; then + docker_push_all "${_docker_repo}" "${_grafana_version}" elif echo "$_grafana_tag" | grep -q "master"; then docker_push_all "grafana/grafana" "master" + docker push "grafana/grafana-dev:${_grafana_version}" fi From a237a495b0b5bb0921735ff560d3ec977e4b90f1 Mon Sep 17 00:00:00 2001 From: Leonard Gram Date: Wed, 9 Jan 2019 16:14:03 +0100 Subject: [PATCH 44/91] Revert "build: fixes docker push." This reverts commit be57f6878cd094893005a179f66de19f17fe9fd5. --- packaging/docker/build.sh | 8 ++++---- packaging/docker/push_to_docker_hub.sh | 11 +++++------ 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/packaging/docker/build.sh b/packaging/docker/build.sh index 1bad2980d34..860df4780b5 100755 --- a/packaging/docker/build.sh +++ b/packaging/docker/build.sh @@ -1,13 +1,14 @@ #!/bin/sh -_grafana_tag=${1:-} -_docker_repo=${2:-grafana/grafana} +_grafana_tag=$1 # If the tag starts with v, treat this as a official release if echo "$_grafana_tag" | grep -q "^v"; then _grafana_version=$(echo "${_grafana_tag}" | cut -d "v" -f 2) + _docker_repo=${2:-grafana/grafana} else _grafana_version=$_grafana_tag + _docker_repo=${2:-grafana/grafana-dev} fi echo "Building ${_docker_repo}:${_grafana_version}" @@ -44,6 +45,5 @@ docker_build "arm64v8/debian:stretch-slim" "grafana-latest.linux-arm64.tar.gz" " if echo "$_grafana_tag" | grep -q "^v"; then docker_tag_all "${_docker_repo}" "latest" else - docker_tag_all "${_docker_repo}" "master" - docker tag "${_docker_repo}:${_grafana_version} grafana/grafana-dev:${_grafana_version}" + docker_tag_all "grafana/grafana" "master" fi diff --git a/packaging/docker/push_to_docker_hub.sh b/packaging/docker/push_to_docker_hub.sh index cef6d596851..b873dbe05e0 100755 --- a/packaging/docker/push_to_docker_hub.sh +++ b/packaging/docker/push_to_docker_hub.sh @@ -1,14 +1,15 @@ #!/bin/sh set -e -_grafana_tag=${1:-} -_docker_repo=${2:-grafana/grafana} +_grafana_tag=$1 # If the tag starts with v, treat this as a official release if echo "$_grafana_tag" | grep -q "^v"; then _grafana_version=$(echo "${_grafana_tag}" | cut -d "v" -f 2) + _docker_repo=${2:-grafana/grafana} else _grafana_version=$_grafana_tag + _docker_repo=${2:-grafana/grafana-dev} fi export DOCKER_CLI_EXPERIMENTAL=enabled @@ -34,13 +35,11 @@ docker_push_all () { docker manifest push "${repo}:${tag}" } +docker_push_all "${_docker_repo}" "${_grafana_version}" + if echo "$_grafana_tag" | grep -q "^v" && echo "$_grafana_tag" | grep -vq "beta"; then echo "pushing ${_docker_repo}:latest" docker_push_all "${_docker_repo}" "latest" - docker_push_all "${_docker_repo}" "${_grafana_version}" -elif echo "$_grafana_tag" | grep -q "^v" && echo "$_grafana_tag" | grep -q "beta"; then - docker_push_all "${_docker_repo}" "${_grafana_version}" elif echo "$_grafana_tag" | grep -q "master"; then docker_push_all "grafana/grafana" "master" - docker push "grafana/grafana-dev:${_grafana_version}" fi From 13a962cc50d149cfdc9dece53adee47eec94f364 Mon Sep 17 00:00:00 2001 From: Leonard Gram Date: Wed, 9 Jan 2019 16:14:41 +0100 Subject: [PATCH 45/91] Revert "Docker image for ARM" This reverts commit 5f7e6a5c7378ebf4fa534e3b3de8b7f293c1f0a3. --- .circleci/config.yml | 44 +++++++++++++------------- build.go | 2 -- packaging/docker/Dockerfile | 6 ++-- packaging/docker/build.sh | 34 +++----------------- packaging/docker/push_to_docker_hub.sh | 29 +++-------------- scripts/build/build.sh | 7 ---- 6 files changed, 33 insertions(+), 89 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 3d66a8ef13b..236d5aec398 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -200,47 +200,47 @@ jobs: - dist/grafana* grafana-docker-master: - machine: - image: circleci/classic:201808-01 + docker: + - image: docker:stable-git steps: - checkout - attach_workspace: at: . + - setup_remote_docker - run: docker info - - run: docker run --privileged linuxkit/binfmt:v0.6 - - run: cp dist/grafana-latest.linux-*.tar.gz packaging/docker + - run: cp dist/grafana-latest.linux-x64.tar.gz packaging/docker - run: cd packaging/docker && ./build-deploy.sh "master-${CIRCLE_SHA1}" - - run: rm packaging/docker/grafana-latest.linux-*.tar.gz + - run: rm packaging/docker/grafana-latest.linux-x64.tar.gz - run: cp enterprise-dist/grafana-enterprise-*.linux-amd64.tar.gz packaging/docker/grafana-latest.linux-x64.tar.gz - run: cd packaging/docker && ./build-enterprise.sh "master" grafana-docker-pr: - machine: - image: circleci/classic:201808-01 + docker: + - image: docker:stable-git steps: - checkout - attach_workspace: at: . + - setup_remote_docker - run: docker info - - run: docker run --privileged linuxkit/binfmt:v0.6 - - run: cp dist/grafana-latest.linux-*.tar.gz packaging/docker + - run: cp dist/grafana-latest.linux-x64.tar.gz packaging/docker - run: cd packaging/docker && ./build.sh "${CIRCLE_SHA1}" grafana-docker-release: - machine: - image: circleci/classic:201808-01 - steps: - - checkout - - attach_workspace: - at: . - - run: docker info - - run: docker run --privileged linuxkit/binfmt:v0.6 - - run: cp dist/grafana-latest.linux-*.tar.gz packaging/docker - - run: cd packaging/docker && ./build-deploy.sh "${CIRCLE_TAG}" - - run: rm packaging/docker/grafana-latest.linux-*.tar.gz - - run: cp enterprise-dist/grafana-enterprise-*.linux-amd64.tar.gz packaging/docker/grafana-latest.linux-x64.tar.gz - - run: cd packaging/docker && ./build-enterprise.sh "${CIRCLE_TAG}" + docker: + - image: docker:stable-git + steps: + - checkout + - attach_workspace: + at: . + - setup_remote_docker + - run: docker info + - run: cp dist/grafana-latest.linux-x64.tar.gz packaging/docker + - run: cd packaging/docker && ./build-deploy.sh "${CIRCLE_TAG}" + - run: rm packaging/docker/grafana-latest.linux-x64.tar.gz + - run: cp enterprise-dist/grafana-enterprise-*.linux-amd64.tar.gz packaging/docker/grafana-latest.linux-x64.tar.gz + - run: cd packaging/docker && ./build-enterprise.sh "${CIRCLE_TAG}" build-enterprise: docker: diff --git a/build.go b/build.go index 4486cd3deb9..9d5216de1d0 100644 --- a/build.go +++ b/build.go @@ -164,8 +164,6 @@ func makeLatestDistCopies() { "_amd64.deb": "dist/grafana_latest_amd64.deb", ".x86_64.rpm": "dist/grafana-latest-1.x86_64.rpm", ".linux-amd64.tar.gz": "dist/grafana-latest.linux-x64.tar.gz", - ".linux-armv7.tar.gz": "dist/grafana-latest.linux-armv7.tar.gz", - ".linux-arm64.tar.gz": "dist/grafana-latest.linux-arm64.tar.gz", } for _, file := range files { diff --git a/packaging/docker/Dockerfile b/packaging/docker/Dockerfile index d4f2f2aa7a3..4d4f6539972 100644 --- a/packaging/docker/Dockerfile +++ b/packaging/docker/Dockerfile @@ -1,5 +1,4 @@ -ARG BASE_IMAGE=debian:stretch-slim -FROM ${BASE_IMAGE} +FROM debian:stretch-slim ARG GRAFANA_TGZ="grafana-latest.linux-x64.tar.gz" @@ -11,8 +10,7 @@ COPY ${GRAFANA_TGZ} /tmp/grafana.tar.gz RUN mkdir /tmp/grafana && tar xfvz /tmp/grafana.tar.gz --strip-components=1 -C /tmp/grafana -ARG BASE_IMAGE=debian:stretch-slim -FROM ${BASE_IMAGE} +FROM debian:stretch-slim ARG GF_UID="472" ARG GF_GID="472" diff --git a/packaging/docker/build.sh b/packaging/docker/build.sh index 860df4780b5..c303c71cd5f 100755 --- a/packaging/docker/build.sh +++ b/packaging/docker/build.sh @@ -13,37 +13,13 @@ fi echo "Building ${_docker_repo}:${_grafana_version}" -export DOCKER_CLI_EXPERIMENTAL=enabled - -# Build grafana image for a specific arch -docker_build () { - base_image=$1 - grafana_tgz=$2 - tag=$3 - - docker build \ - --build-arg BASE_IMAGE=${base_image} \ - --build-arg GRAFANA_TGZ=${grafana_tgz} \ - --tag "${tag}" \ - --no-cache=true . -} - -# Tag docker images of all architectures -docker_tag_all () { - repo=$1 - tag=$2 - docker tag "${_docker_repo}:${_grafana_version}" "${repo}:${tag}" - docker tag "${_docker_repo}-arm32v7-linux:${_grafana_version}" "${repo}-arm32v7-linux:${tag}" - docker tag "${_docker_repo}-arm64v8-linux:${_grafana_version}" "${repo}-arm64v8-linux:${tag}" -} - -docker_build "debian:stretch-slim" "grafana-latest.linux-x64.tar.gz" "${_docker_repo}:${_grafana_version}" -docker_build "arm32v7/debian:stretch-slim" "grafana-latest.linux-armv7.tar.gz" "${_docker_repo}-arm32v7-linux:${_grafana_version}" -docker_build "arm64v8/debian:stretch-slim" "grafana-latest.linux-arm64.tar.gz" "${_docker_repo}-arm64v8-linux:${_grafana_version}" +docker build \ + --tag "${_docker_repo}:${_grafana_version}" \ + --no-cache=true . # Tag as 'latest' for official release; otherwise tag as grafana/grafana:master if echo "$_grafana_tag" | grep -q "^v"; then - docker_tag_all "${_docker_repo}" "latest" + docker tag "${_docker_repo}:${_grafana_version}" "${_docker_repo}:latest" else - docker_tag_all "grafana/grafana" "master" + docker tag "${_docker_repo}:${_grafana_version}" "grafana/grafana:master" fi diff --git a/packaging/docker/push_to_docker_hub.sh b/packaging/docker/push_to_docker_hub.sh index b873dbe05e0..526c216f8fa 100755 --- a/packaging/docker/push_to_docker_hub.sh +++ b/packaging/docker/push_to_docker_hub.sh @@ -12,34 +12,13 @@ else _docker_repo=${2:-grafana/grafana-dev} fi -export DOCKER_CLI_EXPERIMENTAL=enabled - echo "pushing ${_docker_repo}:${_grafana_version}" - - -docker_push_all () { - repo=$1 - tag=$2 - - # Push each image individually - docker push "${repo}:${tag}" - docker push "${repo}-arm32v7-linux:${tag}" - docker push "${repo}-arm64v8-linux:${tag}" - - # Create and push a multi-arch manifest - docker manifest create "${repo}:${tag}" \ - "${repo}:${tag}" \ - "${repo}-arm32v7-linux:${tag}" \ - "${repo}-arm64v8-linux:${tag}" - - docker manifest push "${repo}:${tag}" -} - -docker_push_all "${_docker_repo}" "${_grafana_version}" +docker push "${_docker_repo}:${_grafana_version}" if echo "$_grafana_tag" | grep -q "^v" && echo "$_grafana_tag" | grep -vq "beta"; then echo "pushing ${_docker_repo}:latest" - docker_push_all "${_docker_repo}" "latest" + docker push "${_docker_repo}:latest" elif echo "$_grafana_tag" | grep -q "master"; then - docker_push_all "grafana/grafana" "master" + echo "pushing grafana/grafana:master" + docker push grafana/grafana:master fi diff --git a/scripts/build/build.sh b/scripts/build/build.sh index 1222053f1c8..8362942c6cd 100755 --- a/scripts/build/build.sh +++ b/scripts/build/build.sh @@ -8,8 +8,6 @@ set -e EXTRA_OPTS="$@" -CCARMV7=arm-linux-gnueabihf-gcc -CCARM64=aarch64-linux-gnu-gcc CCX64=/tmp/x86_64-centos6-linux-gnu/bin/x86_64-centos6-linux-gnu-gcc GOPATH=/go @@ -28,9 +26,6 @@ fi echo "Build arguments: $OPT" -go run build.go -goarch armv7 -cc ${CCARMV7} ${OPT} build -go run build.go -goarch arm64 -cc ${CCARM64} ${OPT} build - CC=${CCX64} go run build.go ${OPT} build yarn install --pure-lockfile --no-progress @@ -49,5 +44,3 @@ source /etc/profile.d/rvm.sh echo "Packaging" go run build.go -goos linux -pkg-arch amd64 ${OPT} package-only latest -go run build.go -goos linux -pkg-arch armv7 ${OPT} package-only latest -go run build.go -goos linux -pkg-arch arm64 ${OPT} package-only latest From 1618e90844fa084e762021b1b8f0b421b7993569 Mon Sep 17 00:00:00 2001 From: Dominik Prokop Date: Wed, 9 Jan 2019 17:00:10 +0100 Subject: [PATCH 46/91] Minor refactor of Gauge panel --- .../app/plugins/panel/gauge/GaugeOptions.tsx | 8 +- public/app/plugins/panel/gauge/GaugePanel.tsx | 20 +++++ .../plugins/panel/gauge/GaugePanelOptions.tsx | 46 ++++++++++ .../plugins/panel/gauge/Threshold.test.tsx | 5 +- public/app/plugins/panel/gauge/Thresholds.tsx | 5 +- .../panel/gauge/ValueMappings.test.tsx | 6 +- .../app/plugins/panel/gauge/ValueMappings.tsx | 5 +- .../app/plugins/panel/gauge/ValueOptions.tsx | 5 +- public/app/plugins/panel/gauge/module.tsx | 85 +------------------ public/app/plugins/panel/gauge/types.ts | 16 ++++ 10 files changed, 106 insertions(+), 95 deletions(-) create mode 100644 public/app/plugins/panel/gauge/GaugePanel.tsx create mode 100644 public/app/plugins/panel/gauge/GaugePanelOptions.tsx create mode 100644 public/app/plugins/panel/gauge/types.ts diff --git a/public/app/plugins/panel/gauge/GaugeOptions.tsx b/public/app/plugins/panel/gauge/GaugeOptions.tsx index 655e9b0a65d..7607374b1b7 100644 --- a/public/app/plugins/panel/gauge/GaugeOptions.tsx +++ b/public/app/plugins/panel/gauge/GaugeOptions.tsx @@ -1,9 +1,10 @@ import React, { PureComponent } from 'react'; import { Switch } from 'app/core/components/Switch/Switch'; -import { OptionModuleProps } from './module'; import { Label } from '../../../core/components/Label/Label'; +import { PanelOptionsProps } from '@grafana/ui'; +import { Options } from './types'; -export default class GaugeOptions extends PureComponent { +export default class GaugeOptions extends PureComponent> { onToggleThresholdLabels = () => this.props.onChange({ ...this.props.options, showThresholdLabels: !this.props.options.showThresholdLabels }); @@ -15,7 +16,8 @@ export default class GaugeOptions extends PureComponent { onMaxValueChange = ({ target }) => this.props.onChange({ ...this.props.options, maxValue: target.value }); render() { - const { maxValue, minValue, showThresholdLabels, showThresholdMarkers } = this.props.options; + const { options } = this.props; + const { maxValue, minValue, showThresholdLabels, showThresholdMarkers } = options; return (
diff --git a/public/app/plugins/panel/gauge/GaugePanel.tsx b/public/app/plugins/panel/gauge/GaugePanel.tsx new file mode 100644 index 00000000000..5f1a438863f --- /dev/null +++ b/public/app/plugins/panel/gauge/GaugePanel.tsx @@ -0,0 +1,20 @@ +import React, { PureComponent } from 'react'; +import { PanelProps, NullValueMode } from '@grafana/ui'; +import { getTimeSeriesVMs } from 'app/viz/state/timeSeries'; +import Gauge from 'app/viz/Gauge'; +import { Options } from './types'; + +interface Props extends PanelProps {} + +export class GaugePanel extends PureComponent { + render() { + const { timeSeries, width, height } = this.props; + + const vmSeries = getTimeSeriesVMs({ + timeSeries: timeSeries, + nullValueMode: NullValueMode.Ignore, + }); + + return ; + } +} diff --git a/public/app/plugins/panel/gauge/GaugePanelOptions.tsx b/public/app/plugins/panel/gauge/GaugePanelOptions.tsx new file mode 100644 index 00000000000..2b16ef5a1fe --- /dev/null +++ b/public/app/plugins/panel/gauge/GaugePanelOptions.tsx @@ -0,0 +1,46 @@ +import React, { PureComponent } from 'react'; +import ValueOptions from 'app/plugins/panel/gauge/ValueOptions'; +import Thresholds from 'app/plugins/panel/gauge/Thresholds'; +import { BasicGaugeColor } from 'app/types'; +import { PanelOptionsProps } from '@grafana/ui'; +import ValueMappings from 'app/plugins/panel/gauge/ValueMappings'; +import { Options } from './types'; +import GaugeOptions from './GaugeOptions'; + +export const defaultProps = { + options: { + baseColor: BasicGaugeColor.Green, + minValue: 0, + maxValue: 100, + prefix: '', + showThresholdMarkers: true, + showThresholdLabels: false, + suffix: '', + decimals: 0, + stat: 'avg', + unit: 'none', + mappings: [], + thresholds: [], + }, +}; + +export default class GaugePanelOptions extends PureComponent> { + static defaultProps = defaultProps; + + render() { + const { onChange, options } = this.props; + return ( + <> +
+ + + +
+ +
+ +
+ + ); + } +} diff --git a/public/app/plugins/panel/gauge/Threshold.test.tsx b/public/app/plugins/panel/gauge/Threshold.test.tsx index 3fa508b98a9..852b9f4c104 100644 --- a/public/app/plugins/panel/gauge/Threshold.test.tsx +++ b/public/app/plugins/panel/gauge/Threshold.test.tsx @@ -1,12 +1,13 @@ import React from 'react'; import { shallow } from 'enzyme'; import Thresholds from './Thresholds'; -import { defaultProps, OptionsProps } from './module'; +import { defaultProps } from './GaugePanelOptions'; import { BasicGaugeColor } from 'app/types'; import { PanelOptionsProps } from '@grafana/ui'; +import { Options } from './types'; const setup = (propOverrides?: object) => { - const props: PanelOptionsProps = { + const props: PanelOptionsProps = { onChange: jest.fn(), options: { ...defaultProps.options, diff --git a/public/app/plugins/panel/gauge/Thresholds.tsx b/public/app/plugins/panel/gauge/Thresholds.tsx index dd0dcc1e33b..b4d4930e11d 100644 --- a/public/app/plugins/panel/gauge/Thresholds.tsx +++ b/public/app/plugins/panel/gauge/Thresholds.tsx @@ -1,15 +1,16 @@ import React, { PureComponent } from 'react'; import tinycolor from 'tinycolor2'; import { ColorPicker } from 'app/core/components/colorpicker/ColorPicker'; -import { OptionModuleProps } from './module'; import { BasicGaugeColor, Threshold } from 'app/types'; +import { PanelOptionsProps } from '@grafana/ui'; +import { Options } from './types'; interface State { thresholds: Threshold[]; baseColor: string; } -export default class Thresholds extends PureComponent { +export default class Thresholds extends PureComponent, State> { constructor(props) { super(props); diff --git a/public/app/plugins/panel/gauge/ValueMappings.test.tsx b/public/app/plugins/panel/gauge/ValueMappings.test.tsx index fd9f56343b1..3e59cc76742 100644 --- a/public/app/plugins/panel/gauge/ValueMappings.test.tsx +++ b/public/app/plugins/panel/gauge/ValueMappings.test.tsx @@ -1,11 +1,13 @@ import React from 'react'; import { shallow } from 'enzyme'; import ValueMappings from './ValueMappings'; -import { defaultProps, OptionModuleProps } from './module'; import { MappingType } from 'app/types'; +import { PanelOptionsProps } from '@grafana/ui'; +import { Options } from './types'; +import { defaultProps } from 'app/plugins/panel/gauge/GaugePanelOptions'; const setup = (propOverrides?: object) => { - const props: OptionModuleProps = { + const props: PanelOptionsProps = { onChange: jest.fn(), options: { ...defaultProps.options, diff --git a/public/app/plugins/panel/gauge/ValueMappings.tsx b/public/app/plugins/panel/gauge/ValueMappings.tsx index 2197002a135..be800cf2412 100644 --- a/public/app/plugins/panel/gauge/ValueMappings.tsx +++ b/public/app/plugins/panel/gauge/ValueMappings.tsx @@ -1,14 +1,15 @@ import React, { PureComponent } from 'react'; import MappingRow from './MappingRow'; -import { OptionModuleProps } from './module'; import { MappingType, RangeMap, ValueMap } from 'app/types'; +import { PanelOptionsProps } from '@grafana/ui'; +import { Options } from './types'; interface State { mappings: Array; nextIdToAdd: number; } -export default class ValueMappings extends PureComponent { +export default class ValueMappings extends PureComponent, State> { constructor(props) { super(props); diff --git a/public/app/plugins/panel/gauge/ValueOptions.tsx b/public/app/plugins/panel/gauge/ValueOptions.tsx index 445d6517c5a..4aafc0b0457 100644 --- a/public/app/plugins/panel/gauge/ValueOptions.tsx +++ b/public/app/plugins/panel/gauge/ValueOptions.tsx @@ -2,7 +2,8 @@ import React, { PureComponent } from 'react'; import { Label } from 'app/core/components/Label/Label'; import Select from 'app/core/components/Select/Select'; import UnitPicker from 'app/core/components/Select/UnitPicker'; -import { OptionModuleProps } from './module'; +import { PanelOptionsProps } from '@grafana/ui'; +import { Options } from './types'; const statOptions = [ { value: 'min', label: 'Min' }, @@ -20,7 +21,7 @@ const statOptions = [ const labelWidth = 6; -export default class ValueOptions extends PureComponent { +export default class ValueOptions extends PureComponent> { onUnitChange = unit => this.props.onChange({ ...this.props.options, unit: unit.value }); onStatChange = stat => this.props.onChange({ ...this.props.options, stat: stat.value }); diff --git a/public/app/plugins/panel/gauge/module.tsx b/public/app/plugins/panel/gauge/module.tsx index dccd424b416..783e4825657 100644 --- a/public/app/plugins/panel/gauge/module.tsx +++ b/public/app/plugins/panel/gauge/module.tsx @@ -1,83 +1,4 @@ -import React, { PureComponent } from 'react'; -import Gauge from 'app/viz/Gauge'; -import { getTimeSeriesVMs } from 'app/viz/state/timeSeries'; -import ValueOptions from './ValueOptions'; -import GaugeOptions from './GaugeOptions'; -import Thresholds from './Thresholds'; -import ValueMappings from './ValueMappings'; -import { PanelOptionsProps, PanelProps, NullValueMode } from '@grafana/ui'; -import { BasicGaugeColor, RangeMap, Threshold, ValueMap } from 'app/types'; +import GaugePanelOptions, { defaultProps } from './GaugePanelOptions'; +import { GaugePanel } from './GaugePanel'; -export interface OptionsProps { - baseColor: string; - decimals: number; - mappings: Array; - maxValue: number; - minValue: number; - prefix: string; - showThresholdLabels: boolean; - showThresholdMarkers: boolean; - stat: string; - suffix: string; - thresholds: Threshold[]; - unit: string; -} - -export interface OptionModuleProps { - onChange: (item: any) => void; - options: OptionsProps; -} - -export const defaultProps = { - options: { - baseColor: BasicGaugeColor.Green, - minValue: 0, - maxValue: 100, - prefix: '', - showThresholdMarkers: true, - showThresholdLabels: false, - suffix: '', - decimals: 0, - stat: 'avg', - unit: 'none', - mappings: [], - thresholds: [], - }, -}; - -interface Props extends PanelProps {} - -class GaugePanel extends PureComponent { - render() { - const { timeSeries, width, height } = this.props; - - const vmSeries = getTimeSeriesVMs({ - timeSeries: timeSeries, - nullValueMode: NullValueMode.Ignore, - }); - - return ; - } -} - -class Options extends PureComponent> { - static defaultProps = defaultProps; - - render() { - const { onChange, options } = this.props; - return ( -
-
- - - -
-
- -
-
- ); - } -} - -export { GaugePanel as Panel, Options as PanelOptions, defaultProps as PanelDefaults }; +export { GaugePanel as Panel, GaugePanelOptions as PanelOptions, defaultProps as PanelDefaults }; diff --git a/public/app/plugins/panel/gauge/types.ts b/public/app/plugins/panel/gauge/types.ts new file mode 100644 index 00000000000..60c4fd1581d --- /dev/null +++ b/public/app/plugins/panel/gauge/types.ts @@ -0,0 +1,16 @@ +import { RangeMap, ValueMap, Threshold } from 'app/types'; + +export interface Options { + baseColor: string; + decimals: number; + mappings: Array; + maxValue: number; + minValue: number; + prefix: string; + showThresholdLabels: boolean; + showThresholdMarkers: boolean; + stat: string; + suffix: string; + thresholds: Threshold[]; + unit: string; +} From cdc99e129fec9e9f3d0f012ead6b6ddb3b2d711f Mon Sep 17 00:00:00 2001 From: Dominik Prokop Date: Wed, 9 Jan 2019 17:21:14 +0100 Subject: [PATCH 47/91] React graph panel options component rename --- .../panel/graph2/{GraphOptions.tsx => GraphPanelOptions.tsx} | 2 +- public/app/plugins/panel/graph2/module.tsx | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) rename public/app/plugins/panel/graph2/{GraphOptions.tsx => GraphPanelOptions.tsx} (95%) diff --git a/public/app/plugins/panel/graph2/GraphOptions.tsx b/public/app/plugins/panel/graph2/GraphPanelOptions.tsx similarity index 95% rename from public/app/plugins/panel/graph2/GraphOptions.tsx rename to public/app/plugins/panel/graph2/GraphPanelOptions.tsx index 6bb4b2c13d5..32e68b7a1d4 100644 --- a/public/app/plugins/panel/graph2/GraphOptions.tsx +++ b/public/app/plugins/panel/graph2/GraphPanelOptions.tsx @@ -9,7 +9,7 @@ import { Switch } from 'app/core/components/Switch/Switch'; import { PanelOptionsProps } from '@grafana/ui'; import { Options } from './types'; -export class GraphOptions extends PureComponent> { +export class GraphPanelOptions extends PureComponent> { onToggleLines = () => { this.props.onChange({ ...this.props.options, showLines: !this.props.options.showLines }); }; diff --git a/public/app/plugins/panel/graph2/module.tsx b/public/app/plugins/panel/graph2/module.tsx index ba761ca92cb..762d5609541 100644 --- a/public/app/plugins/panel/graph2/module.tsx +++ b/public/app/plugins/panel/graph2/module.tsx @@ -1,4 +1,4 @@ import { GraphPanel } from './GraphPanel'; -import { GraphOptions } from './GraphOptions'; +import { GraphPanelOptions } from './GraphPanelOptions'; -export { GraphPanel as Panel, GraphOptions as PanelOptions }; +export { GraphPanel as Panel, GraphPanelOptions as PanelOptions }; From 78fe2db5e5264d04c143f0ae136f7eb137991128 Mon Sep 17 00:00:00 2001 From: Peter Holmberg Date: Wed, 9 Jan 2019 21:35:01 +0000 Subject: [PATCH 48/91] 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 49/91] 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 50/91] 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 e1f6870fce9a7d564c54c61152a561d138fa2d73 Mon Sep 17 00:00:00 2001 From: SamuelToh Date: Wed, 9 Jan 2019 16:46:20 +1000 Subject: [PATCH 51/91] 4075: Interpolate tempvar on alias --- .../datasource/elasticsearch/datasource.ts | 4 ++++ .../elasticsearch/specs/datasource.test.ts | 21 +++++++++++++++---- 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/public/app/plugins/datasource/elasticsearch/datasource.ts b/public/app/plugins/datasource/elasticsearch/datasource.ts index c2f2364d49d..3781a9048a6 100644 --- a/public/app/plugins/datasource/elasticsearch/datasource.ts +++ b/public/app/plugins/datasource/elasticsearch/datasource.ts @@ -254,6 +254,10 @@ export class ElasticDatasource { continue; } + if (target.alias) { + target.alias = this.templateSrv.replace(target.alias, options.scopedVars, 'lucene'); + } + const queryString = this.templateSrv.replace(target.query || '*', options.scopedVars, 'lucene'); const queryObj = this.queryBuilder.build(target, adhocFilters, queryString); const esQuery = angular.toJson(queryObj); diff --git a/public/app/plugins/datasource/elasticsearch/specs/datasource.test.ts b/public/app/plugins/datasource/elasticsearch/specs/datasource.test.ts index 4be0c35852c..0480fcb52e8 100644 --- a/public/app/plugins/datasource/elasticsearch/specs/datasource.test.ts +++ b/public/app/plugins/datasource/elasticsearch/specs/datasource.test.ts @@ -16,7 +16,13 @@ describe('ElasticDatasource', function(this: any) { }; const templateSrv = { - replace: jest.fn(text => text), + replace: jest.fn(text => { + if (text.startsWith("$")) { + return `resolvedVariable`; + } else { + return text; + } + }), getAdhocFilters: jest.fn(() => []), }; @@ -67,7 +73,7 @@ describe('ElasticDatasource', function(this: any) { }); describe('When issuing metric query with interval pattern', () => { - let requestOptions, parts, header; + let requestOptions, parts, header, query; beforeEach(() => { createDatasource({ @@ -81,19 +87,22 @@ describe('ElasticDatasource', function(this: any) { return Promise.resolve({ data: { responses: [] } }); }); - ctx.ds.query({ + query = { range: { from: moment.utc([2015, 4, 30, 10]), to: moment.utc([2015, 5, 1, 10]), }, targets: [ { + alias: "$varAlias", bucketAggs: [], metrics: [{ type: 'raw_document' }], query: 'escape\\:test', }, ], - }); + }; + + ctx.ds.query(query); parts = requestOptions.data.split('\n'); header = angular.fromJson(parts[0]); @@ -103,6 +112,10 @@ describe('ElasticDatasource', function(this: any) { expect(header.index).toEqual(['asd-2015.05.30', 'asd-2015.05.31', 'asd-2015.06.01']); }); + it('should resolve the alias variable', () => { + expect(query.targets[0].alias).toEqual('resolvedVariable'); + }); + it('should json escape lucene query', () => { const body = angular.fromJson(parts[1]); expect(body.query.bool.filter[1].query_string.query).toBe('escape\\:test'); From 9e3ab71e403298b9ffa7710e84a8e6b47b336dcc Mon Sep 17 00:00:00 2001 From: SamuelToh Date: Thu, 10 Jan 2019 10:47:22 +1000 Subject: [PATCH 52/91] 11503: escape measurement filter regex value --- public/app/plugins/datasource/influxdb/query_builder.ts | 3 ++- .../plugins/datasource/influxdb/specs/query_builder.test.ts | 6 ++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/public/app/plugins/datasource/influxdb/query_builder.ts b/public/app/plugins/datasource/influxdb/query_builder.ts index a61216787d3..0b3e6f01e74 100644 --- a/public/app/plugins/datasource/influxdb/query_builder.ts +++ b/public/app/plugins/datasource/influxdb/query_builder.ts @@ -1,4 +1,5 @@ import _ from 'lodash'; +import kbn from 'app/core/utils/kbn'; function renderTagCondition(tag, index) { let str = ''; @@ -43,7 +44,7 @@ export class InfluxQueryBuilder { } else if (type === 'MEASUREMENTS') { query = 'SHOW MEASUREMENTS'; if (withMeasurementFilter) { - query += ' WITH MEASUREMENT =~ /' + withMeasurementFilter + '/'; + query += ' WITH MEASUREMENT =~ /' + kbn.regexEscape(withMeasurementFilter) + '/'; } } else if (type === 'FIELDS') { measurement = this.target.measurement; diff --git a/public/app/plugins/datasource/influxdb/specs/query_builder.test.ts b/public/app/plugins/datasource/influxdb/specs/query_builder.test.ts index e21b95ac374..ee617e7e774 100644 --- a/public/app/plugins/datasource/influxdb/specs/query_builder.test.ts +++ b/public/app/plugins/datasource/influxdb/specs/query_builder.test.ts @@ -50,6 +50,12 @@ describe('InfluxQueryBuilder', () => { expect(query).toBe('SHOW MEASUREMENTS WITH MEASUREMENT =~ /something/ LIMIT 100'); }); + it('should escape the regex value in measurement query', () => { + const builder = new InfluxQueryBuilder({ measurement: '', tags: [] }); + const query = builder.buildExploreQuery('MEASUREMENTS', undefined, 'abc/edf/'); + expect(query).toBe('SHOW MEASUREMENTS WITH MEASUREMENT =~ /abc\\/edf\\// LIMIT 100'); + }); + it('should have WITH MEASUREMENT WHERE in measurement query for non-empty query with tags', () => { const builder = new InfluxQueryBuilder({ measurement: '', From 1dc1af7e00823e1107fc683b1e40ad5f86ba1743 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Thu, 10 Jan 2019 08:37:40 +0100 Subject: [PATCH 53/91] Update README.md --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 1ce4ffbe109..ff5da04f209 100644 --- a/README.md +++ b/README.md @@ -131,7 +131,9 @@ GRAFANA_TEST_DB=postgres go test ./pkg/... If you have any idea for an improvement or found a bug, do not hesitate to open an issue. And if you have time clone this repo and submit a pull request and help me make Grafana -the kickass metrics & devops dashboard we all dream about! +the kickass metrics & devops dashboard we all dream about! + +Read the [contributing](https://github.com/grafana/grafana/blob/master/CONTRIBUTING.md) guide then check the [`beginner friendly`](https://github.com/grafana/grafana/issues?q=is%3Aopen+is%3Aissue+label%3A%22beginner+friendly%22) label to find issues that are easy and that we would like help with. ## Plugin development From 5fc07663410722713c54a457fa0ae1a9c7c871e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hugo=20H=C3=A4ggmark?= Date: Thu, 10 Jan 2019 09:37:32 +0100 Subject: [PATCH 54/91] Moved Thresholds and styles to grafana/ui/components --- packages/grafana-ui/package.json | 3 ++- .../src/components/Thresholds}/Threshold.test.tsx | 8 ++++---- .../grafana-ui/src/components/Thresholds}/Thresholds.tsx | 8 ++++---- .../grafana-ui/src/components/Thresholds/_Thresholds.scss | 0 packages/grafana-ui/src/components/index.scss | 1 + packages/grafana-ui/src/components/index.ts | 1 + packages/grafana-ui/src/types/panel.ts | 6 ++++++ public/app/plugins/panel/gauge/GaugePanelOptions.tsx | 3 +-- public/app/types/index.ts | 3 ++- public/app/types/panel.ts | 6 ------ public/sass/_grafana.scss | 1 - 11 files changed, 21 insertions(+), 19 deletions(-) rename {public/app/plugins/panel/gauge => packages/grafana-ui/src/components/Thresholds}/Threshold.test.tsx (91%) rename {public/app/plugins/panel/gauge => packages/grafana-ui/src/components/Thresholds}/Thresholds.tsx (96%) rename public/sass/components/_thresholds.scss => packages/grafana-ui/src/components/Thresholds/_Thresholds.scss (100%) diff --git a/packages/grafana-ui/package.json b/packages/grafana-ui/package.json index 6548f75e91f..724c3334643 100644 --- a/packages/grafana-ui/package.json +++ b/packages/grafana-ui/package.json @@ -23,7 +23,8 @@ "react-highlight-words": "0.11.0", "react-popper": "^1.3.0", "react-transition-group": "^2.2.1", - "react-virtualized": "^9.21.0" + "react-virtualized": "^9.21.0", + "tinycolor2": "^1.4.1" }, "devDependencies": { "@types/classnames": "^2.2.6", diff --git a/public/app/plugins/panel/gauge/Threshold.test.tsx b/packages/grafana-ui/src/components/Thresholds/Threshold.test.tsx similarity index 91% rename from public/app/plugins/panel/gauge/Threshold.test.tsx rename to packages/grafana-ui/src/components/Thresholds/Threshold.test.tsx index 852b9f4c104..eac82e1b0f4 100644 --- a/public/app/plugins/panel/gauge/Threshold.test.tsx +++ b/packages/grafana-ui/src/components/Thresholds/Threshold.test.tsx @@ -1,10 +1,10 @@ import React from 'react'; import { shallow } from 'enzyme'; -import Thresholds from './Thresholds'; -import { defaultProps } from './GaugePanelOptions'; -import { BasicGaugeColor } from 'app/types'; import { PanelOptionsProps } from '@grafana/ui'; -import { Options } from './types'; +import { defaultProps } from 'app/plugins/panel/gauge/GaugePanelOptions'; +import { Options } from 'app/plugins/panel/gauge/types'; +import { BasicGaugeColor } from 'app/types'; +import { Thresholds } from './Thresholds'; const setup = (propOverrides?: object) => { const props: PanelOptionsProps = { diff --git a/public/app/plugins/panel/gauge/Thresholds.tsx b/packages/grafana-ui/src/components/Thresholds/Thresholds.tsx similarity index 96% rename from public/app/plugins/panel/gauge/Thresholds.tsx rename to packages/grafana-ui/src/components/Thresholds/Thresholds.tsx index b4d4930e11d..802891998e9 100644 --- a/public/app/plugins/panel/gauge/Thresholds.tsx +++ b/packages/grafana-ui/src/components/Thresholds/Thresholds.tsx @@ -1,16 +1,16 @@ import React, { PureComponent } from 'react'; import tinycolor from 'tinycolor2'; import { ColorPicker } from 'app/core/components/colorpicker/ColorPicker'; -import { BasicGaugeColor, Threshold } from 'app/types'; -import { PanelOptionsProps } from '@grafana/ui'; -import { Options } from './types'; +import { BasicGaugeColor } from 'app/types'; +import { PanelOptionsProps, Threshold } from '@grafana/ui'; +import { Options } from 'app/plugins/panel/gauge/types'; interface State { thresholds: Threshold[]; baseColor: string; } -export default class Thresholds extends PureComponent, State> { +export class Thresholds extends PureComponent, State> { constructor(props) { super(props); diff --git a/public/sass/components/_thresholds.scss b/packages/grafana-ui/src/components/Thresholds/_Thresholds.scss similarity index 100% rename from public/sass/components/_thresholds.scss rename to packages/grafana-ui/src/components/Thresholds/_Thresholds.scss diff --git a/packages/grafana-ui/src/components/index.scss b/packages/grafana-ui/src/components/index.scss index e1d1474bb16..d0a81675490 100644 --- a/packages/grafana-ui/src/components/index.scss +++ b/packages/grafana-ui/src/components/index.scss @@ -1,3 +1,4 @@ @import 'CustomScrollbar/CustomScrollbar'; @import 'DeleteButton/DeleteButton'; +@import 'Thresholds/Thresholds'; @import 'Tooltip/Tooltip'; diff --git a/packages/grafana-ui/src/components/index.ts b/packages/grafana-ui/src/components/index.ts index abb1cf1b34c..5293cb7bc66 100644 --- a/packages/grafana-ui/src/components/index.ts +++ b/packages/grafana-ui/src/components/index.ts @@ -2,3 +2,4 @@ export { DeleteButton } from './DeleteButton/DeleteButton'; export { Tooltip } from './Tooltip/Tooltip'; export { Portal } from './Portal/Portal'; export { CustomScrollbar } from './CustomScrollbar/CustomScrollbar'; +export { Thresholds } from './Thresholds/Thresholds'; diff --git a/packages/grafana-ui/src/types/panel.ts b/packages/grafana-ui/src/types/panel.ts index 44336555a81..46fe84a211c 100644 --- a/packages/grafana-ui/src/types/panel.ts +++ b/packages/grafana-ui/src/types/panel.ts @@ -29,3 +29,9 @@ export interface PanelMenuItem { shortcut?: string; subMenu?: PanelMenuItem[]; } + +export interface Threshold { + index: number; + value: number; + color?: string; +} diff --git a/public/app/plugins/panel/gauge/GaugePanelOptions.tsx b/public/app/plugins/panel/gauge/GaugePanelOptions.tsx index 2b16ef5a1fe..7b627a09592 100644 --- a/public/app/plugins/panel/gauge/GaugePanelOptions.tsx +++ b/public/app/plugins/panel/gauge/GaugePanelOptions.tsx @@ -1,8 +1,7 @@ import React, { PureComponent } from 'react'; import ValueOptions from 'app/plugins/panel/gauge/ValueOptions'; -import Thresholds from 'app/plugins/panel/gauge/Thresholds'; import { BasicGaugeColor } from 'app/types'; -import { PanelOptionsProps } from '@grafana/ui'; +import { PanelOptionsProps, Thresholds } from '@grafana/ui'; import ValueMappings from 'app/plugins/panel/gauge/ValueMappings'; import { Options } from './types'; import GaugeOptions from './GaugeOptions'; diff --git a/public/app/types/index.ts b/public/app/types/index.ts index ab52b03ab17..52b2b996542 100644 --- a/public/app/types/index.ts +++ b/public/app/types/index.ts @@ -9,7 +9,7 @@ import { ApiKey, ApiKeysState, NewApiKey } from './apiKeys'; import { Invitee, OrgUser, User, UsersState, UserState } from './user'; import { DataSource, DataSourceSelectItem, DataSourcesState } from './datasources'; import { DataQuery, DataQueryResponse, DataQueryOptions } from './series'; -import { BasicGaugeColor, MappingType, RangeMap, Threshold, ValueMap } from './panel'; +import { BasicGaugeColor, MappingType, RangeMap, ValueMap } from './panel'; import { PluginDashboard, PluginMeta, Plugin, PanelPlugin, PluginsState } from './plugins'; import { Organization, OrganizationState } from './organization'; import { @@ -20,6 +20,7 @@ import { } from './appNotifications'; import { DashboardSearchHit } from './search'; import { ValidationEvents, ValidationRule } from './form'; +import { Threshold } from '@grafana/ui'; export { Team, TeamsState, diff --git a/public/app/types/panel.ts b/public/app/types/panel.ts index 31674d20304..1f5a2307733 100644 --- a/public/app/types/panel.ts +++ b/public/app/types/panel.ts @@ -1,9 +1,3 @@ -export interface Threshold { - index: number; - value: number; - color?: string; -} - export enum MappingType { ValueToText = 1, RangeToText = 2, diff --git a/public/sass/_grafana.scss b/public/sass/_grafana.scss index 10cc7335bdf..a3dd204eb63 100644 --- a/public/sass/_grafana.scss +++ b/public/sass/_grafana.scss @@ -102,7 +102,6 @@ @import 'components/toolbar'; @import 'components/add_data_source.scss'; @import 'components/page_loader'; -@import 'components/thresholds'; @import 'components/toggle_button_group'; @import 'components/value-mappings'; @import 'components/popover-box'; From 96759e39a63a2934a6cbbe34434a33d9767ceb53 Mon Sep 17 00:00:00 2001 From: Daniel Lee Date: Thu, 10 Jan 2019 09:47:39 +0100 Subject: [PATCH 55/91] docker: enable flux in influxdb docker block --- devenv/docker/blocks/influxdb/influxdb.conf | 1 + 1 file changed, 1 insertion(+) diff --git a/devenv/docker/blocks/influxdb/influxdb.conf b/devenv/docker/blocks/influxdb/influxdb.conf index c0331ce7449..120739dd896 100644 --- a/devenv/docker/blocks/influxdb/influxdb.conf +++ b/devenv/docker/blocks/influxdb/influxdb.conf @@ -69,6 +69,7 @@ reporting-disabled = false unix-socket-enabled = false # enable http service over unix domain socket # bind-socket = "/var/run/influxdb.sock" + flux-enabled = true [subscriber] enabled = true From 7819529d459c6c20aab524c26528212f754b72fb Mon Sep 17 00:00:00 2001 From: Dominik Prokop Date: Thu, 10 Jan 2019 13:29:53 +0100 Subject: [PATCH 56/91] Migrate Select components to @grafana/ui --- .../components/Select/IndicatorsContainer.tsx | 2 +- .../components/Select/NoOptionsMessage.tsx | 0 .../src}/components/Select/OptionGroup.tsx | 2 +- .../components/Select/PickerOption.test.tsx | 34 +++++++++++++------ .../src}/components/Select/PickerOption.tsx | 2 +- .../src}/components/Select/Select.tsx | 8 ++--- .../components/Select/resetSelectStyles.ts | 27 +++++++++++++++ packages/grafana-ui/src/components/index.ts | 6 ++++ .../PermissionList/AddPermission.tsx | 2 +- .../DisabledPermissionListItem.tsx | 2 +- .../PermissionList/PermissionListItem.tsx | 2 +- .../components/Select/DataSourcePicker.tsx | 2 +- .../core/components/Select/ResetStyles.tsx | 25 -------------- .../app/core/components/Select/TeamPicker.tsx | 2 +- .../app/core/components/Select/UnitPicker.tsx | 2 +- .../app/core/components/Select/UserPicker.tsx | 2 +- .../SharedPreferences/SharedPreferences.tsx | 2 +- .../core/components/TagFilter/TagFilter.tsx | 6 ++-- public/app/plugins/panel/gauge/MappingRow.tsx | 2 +- .../app/plugins/panel/gauge/ValueOptions.tsx | 2 +- yarn.lock | 6 ++-- 21 files changed, 78 insertions(+), 60 deletions(-) rename {public/app/core => packages/grafana-ui/src}/components/Select/IndicatorsContainer.tsx (88%) rename {public/app/core => packages/grafana-ui/src}/components/Select/NoOptionsMessage.tsx (100%) rename {public/app/core => packages/grafana-ui/src}/components/Select/OptionGroup.tsx (96%) rename {public/app/core => packages/grafana-ui/src}/components/Select/PickerOption.test.tsx (55%) rename {public/app/core => packages/grafana-ui/src}/components/Select/PickerOption.tsx (96%) rename {public/app/core => packages/grafana-ui/src}/components/Select/Select.tsx (97%) create mode 100644 packages/grafana-ui/src/components/Select/resetSelectStyles.ts delete mode 100644 public/app/core/components/Select/ResetStyles.tsx diff --git a/public/app/core/components/Select/IndicatorsContainer.tsx b/packages/grafana-ui/src/components/Select/IndicatorsContainer.tsx similarity index 88% rename from public/app/core/components/Select/IndicatorsContainer.tsx rename to packages/grafana-ui/src/components/Select/IndicatorsContainer.tsx index d4de51a2cef..8fc8e0b08d3 100644 --- a/public/app/core/components/Select/IndicatorsContainer.tsx +++ b/packages/grafana-ui/src/components/Select/IndicatorsContainer.tsx @@ -1,7 +1,7 @@ import React from 'react'; import { components } from '@torkelo/react-select'; -export const IndicatorsContainer = props => { +export const IndicatorsContainer = (props: any) => { const isOpen = props.selectProps.menuIsOpen; return ( diff --git a/public/app/core/components/Select/NoOptionsMessage.tsx b/packages/grafana-ui/src/components/Select/NoOptionsMessage.tsx similarity index 100% rename from public/app/core/components/Select/NoOptionsMessage.tsx rename to packages/grafana-ui/src/components/Select/NoOptionsMessage.tsx diff --git a/public/app/core/components/Select/OptionGroup.tsx b/packages/grafana-ui/src/components/Select/OptionGroup.tsx similarity index 96% rename from public/app/core/components/Select/OptionGroup.tsx rename to packages/grafana-ui/src/components/Select/OptionGroup.tsx index a001f58c681..ed2b72a537c 100644 --- a/public/app/core/components/Select/OptionGroup.tsx +++ b/packages/grafana-ui/src/components/Select/OptionGroup.tsx @@ -24,7 +24,7 @@ export default class OptionGroup extends PureComponent = { cx: jest.fn(), clearValue: jest.fn(), - onSelect: jest.fn(), getStyles: jest.fn(), getValue: jest.fn(), hasValue: true, @@ -18,21 +18,33 @@ const model = { isFocused: false, isSelected: false, innerRef: null, - innerProps: null, - label: 'Option label', - type: null, - children: 'Model title', - data: { - title: 'Model title', - imgUrl: 'url/to/avatar', - label: 'User picker label', + innerProps: { + id: '', + key: '', + onClick: jest.fn(), + onMouseOver: jest.fn(), + tabIndex: 1, }, + label: 'Option label', + type: 'option', + children: 'Model title', className: 'class-for-user-picker', }; describe('PickerOption', () => { it('renders correctly', () => { - const tree = renderer.create().toJSON(); + const tree = renderer + .create( + + ) + .toJSON(); expect(tree).toMatchSnapshot(); }); }); diff --git a/public/app/core/components/Select/PickerOption.tsx b/packages/grafana-ui/src/components/Select/PickerOption.tsx similarity index 96% rename from public/app/core/components/Select/PickerOption.tsx rename to packages/grafana-ui/src/components/Select/PickerOption.tsx index d263f6f832b..ac6a5c62783 100644 --- a/public/app/core/components/Select/PickerOption.tsx +++ b/packages/grafana-ui/src/components/Select/PickerOption.tsx @@ -28,7 +28,7 @@ export const Option = (props: ExtendedOptionProps) => { }; // was not able to type this without typescript error -export const SingleValue = props => { +export const SingleValue = (props: any) => { const { children, data } = props; return ( diff --git a/public/app/core/components/Select/Select.tsx b/packages/grafana-ui/src/components/Select/Select.tsx similarity index 97% rename from public/app/core/components/Select/Select.tsx rename to packages/grafana-ui/src/components/Select/Select.tsx index f66e07c9ed6..c456de1c94d 100644 --- a/public/app/core/components/Select/Select.tsx +++ b/packages/grafana-ui/src/components/Select/Select.tsx @@ -10,7 +10,7 @@ import { Option, SingleValue } from './PickerOption'; import OptionGroup from './OptionGroup'; import IndicatorsContainer from './IndicatorsContainer'; import NoOptionsMessage from './NoOptionsMessage'; -import ResetStyles from './ResetStyles'; +import resetSelectStyles from './resetSelectStyles'; import { CustomScrollbar } from '@grafana/ui'; export interface SelectOptionItem { @@ -53,7 +53,7 @@ interface AsyncProps { loadingMessage?: () => string; } -export const MenuList = props => { +export const MenuList = (props: any) => { return ( {props.children} @@ -127,7 +127,7 @@ export class Select extends PureComponent { onChange={onChange} options={options} placeholder={placeholder || 'Choose'} - styles={ResetStyles} + styles={resetSelectStyles()} isDisabled={isDisabled} isLoading={isLoading} isClearable={isClearable} @@ -212,7 +212,7 @@ export class AsyncSelect extends PureComponent { isLoading={isLoading} defaultOptions={defaultOptions} placeholder={placeholder || 'Choose'} - styles={ResetStyles} + styles={resetSelectStyles()} loadingMessage={loadingMessage} noOptionsMessage={noOptionsMessage} isDisabled={isDisabled} diff --git a/packages/grafana-ui/src/components/Select/resetSelectStyles.ts b/packages/grafana-ui/src/components/Select/resetSelectStyles.ts new file mode 100644 index 00000000000..a980741c17c --- /dev/null +++ b/packages/grafana-ui/src/components/Select/resetSelectStyles.ts @@ -0,0 +1,27 @@ +export default function resetSelectStyles() { + return { + clearIndicator: () => ({}), + container: () => ({}), + control: () => ({}), + dropdownIndicator: () => ({}), + group: () => ({}), + groupHeading: () => ({}), + indicatorsContainer: () => ({}), + indicatorSeparator: () => ({}), + input: () => ({}), + loadingIndicator: () => ({}), + loadingMessage: () => ({}), + menu: () => ({}), + menuList: ({ maxHeight }: { maxHeight: number }) => ({ + maxHeight, + }), + multiValue: () => ({}), + multiValueLabel: () => ({}), + multiValueRemove: () => ({}), + noOptionsMessage: () => ({}), + option: () => ({}), + placeholder: () => ({}), + singleValue: () => ({}), + valueContainer: () => ({}), + }; +} diff --git a/packages/grafana-ui/src/components/index.ts b/packages/grafana-ui/src/components/index.ts index abb1cf1b34c..9780b841959 100644 --- a/packages/grafana-ui/src/components/index.ts +++ b/packages/grafana-ui/src/components/index.ts @@ -2,3 +2,9 @@ export { DeleteButton } from './DeleteButton/DeleteButton'; export { Tooltip } from './Tooltip/Tooltip'; export { Portal } from './Portal/Portal'; export { CustomScrollbar } from './CustomScrollbar/CustomScrollbar'; + +// Select +export { Select, AsyncSelect, SelectOptionItem } from './Select/Select'; +export { IndicatorsContainer } from './Select/IndicatorsContainer'; +export { NoOptionsMessage } from './Select/NoOptionsMessage'; +export { default as resetSelectStyles } from './Select/resetSelectStyles'; diff --git a/public/app/core/components/PermissionList/AddPermission.tsx b/public/app/core/components/PermissionList/AddPermission.tsx index 749bef680bf..30219371257 100644 --- a/public/app/core/components/PermissionList/AddPermission.tsx +++ b/public/app/core/components/PermissionList/AddPermission.tsx @@ -1,7 +1,7 @@ import React, { Component } from 'react'; import { UserPicker } from 'app/core/components/Select/UserPicker'; import { TeamPicker, Team } from 'app/core/components/Select/TeamPicker'; -import { Select, SelectOptionItem } from 'app/core/components/Select/Select'; +import { Select, SelectOptionItem } from '@grafana/ui'; import { User } from 'app/types'; import { dashboardPermissionLevels, diff --git a/public/app/core/components/PermissionList/DisabledPermissionListItem.tsx b/public/app/core/components/PermissionList/DisabledPermissionListItem.tsx index d3f9ddbb1fb..ebf3cbad1bc 100644 --- a/public/app/core/components/PermissionList/DisabledPermissionListItem.tsx +++ b/public/app/core/components/PermissionList/DisabledPermissionListItem.tsx @@ -1,5 +1,5 @@ import React, { Component } from 'react'; -import Select from 'app/core/components/Select/Select'; +import { Select } from '@grafana/ui'; import { dashboardPermissionLevels } from 'app/types/acl'; export interface Props { diff --git a/public/app/core/components/PermissionList/PermissionListItem.tsx b/public/app/core/components/PermissionList/PermissionListItem.tsx index e726667cfbb..c33b564154a 100644 --- a/public/app/core/components/PermissionList/PermissionListItem.tsx +++ b/public/app/core/components/PermissionList/PermissionListItem.tsx @@ -1,5 +1,5 @@ import React, { PureComponent } from 'react'; -import { Select } from 'app/core/components/Select/Select'; +import { Select } from '@grafana/ui'; import { dashboardPermissionLevels, DashboardAcl, PermissionLevel } from 'app/types/acl'; import { FolderInfo } from 'app/types'; diff --git a/public/app/core/components/Select/DataSourcePicker.tsx b/public/app/core/components/Select/DataSourcePicker.tsx index 1a9081038c0..372c4cd4013 100644 --- a/public/app/core/components/Select/DataSourcePicker.tsx +++ b/public/app/core/components/Select/DataSourcePicker.tsx @@ -3,7 +3,7 @@ import React, { PureComponent } from 'react'; import _ from 'lodash'; // Components -import Select from './Select'; +import { Select } from '@grafana/ui'; // Types import { DataSourceSelectItem } from 'app/types'; diff --git a/public/app/core/components/Select/ResetStyles.tsx b/public/app/core/components/Select/ResetStyles.tsx deleted file mode 100644 index c34abb544ab..00000000000 --- a/public/app/core/components/Select/ResetStyles.tsx +++ /dev/null @@ -1,25 +0,0 @@ -export default { - clearIndicator: () => ({}), - container: () => ({}), - control: () => ({}), - dropdownIndicator: () => ({}), - group: () => ({}), - groupHeading: () => ({}), - indicatorsContainer: () => ({}), - indicatorSeparator: () => ({}), - input: () => ({}), - loadingIndicator: () => ({}), - loadingMessage: () => ({}), - menu: () => ({}), - menuList: ({ maxHeight }: { maxHeight: number }) => ({ - maxHeight, - }), - multiValue: () => ({}), - multiValueLabel: () => ({}), - multiValueRemove: () => ({}), - noOptionsMessage: () => ({}), - option: () => ({}), - placeholder: () => ({}), - singleValue: () => ({}), - valueContainer: () => ({}), -}; diff --git a/public/app/core/components/Select/TeamPicker.tsx b/public/app/core/components/Select/TeamPicker.tsx index bc608318806..8d9e1d48d81 100644 --- a/public/app/core/components/Select/TeamPicker.tsx +++ b/public/app/core/components/Select/TeamPicker.tsx @@ -1,6 +1,6 @@ import React, { Component } from 'react'; import _ from 'lodash'; -import { AsyncSelect } from './Select'; +import { AsyncSelect } from '@grafana/ui'; import { debounce } from 'lodash'; import { getBackendSrv } from 'app/core/services/backend_srv'; diff --git a/public/app/core/components/Select/UnitPicker.tsx b/public/app/core/components/Select/UnitPicker.tsx index 75885cbbb84..54b064df7dc 100644 --- a/public/app/core/components/Select/UnitPicker.tsx +++ b/public/app/core/components/Select/UnitPicker.tsx @@ -1,5 +1,5 @@ import React, { PureComponent } from 'react'; -import Select from './Select'; +import { Select } from '@grafana/ui'; import kbn from 'app/core/utils/kbn'; interface Props { diff --git a/public/app/core/components/Select/UserPicker.tsx b/public/app/core/components/Select/UserPicker.tsx index 8496d707105..ff4ae32f068 100644 --- a/public/app/core/components/Select/UserPicker.tsx +++ b/public/app/core/components/Select/UserPicker.tsx @@ -3,7 +3,7 @@ import React, { Component } from 'react'; import _ from 'lodash'; // Components -import { AsyncSelect } from './Select'; +import { AsyncSelect } from '@grafana/ui'; // Utils & Services import { debounce } from 'lodash'; diff --git a/public/app/core/components/SharedPreferences/SharedPreferences.tsx b/public/app/core/components/SharedPreferences/SharedPreferences.tsx index d41626d9a2f..b13393ab2e1 100644 --- a/public/app/core/components/SharedPreferences/SharedPreferences.tsx +++ b/public/app/core/components/SharedPreferences/SharedPreferences.tsx @@ -1,7 +1,7 @@ import React, { PureComponent } from 'react'; import { Label } from 'app/core/components/Label/Label'; -import Select from 'app/core/components/Select/Select'; +import { Select } from '@grafana/ui'; import { getBackendSrv, BackendSrv } from 'app/core/services/backend_srv'; import { DashboardSearchHit } from 'app/types'; diff --git a/public/app/core/components/TagFilter/TagFilter.tsx b/public/app/core/components/TagFilter/TagFilter.tsx index 4b2de6b1b16..7e8bc9c6fd2 100644 --- a/public/app/core/components/TagFilter/TagFilter.tsx +++ b/public/app/core/components/TagFilter/TagFilter.tsx @@ -1,12 +1,10 @@ import React from 'react'; +import { NoOptionsMessage, IndicatorsContainer, resetSelectStyles } from '@grafana/ui'; import AsyncSelect from '@torkelo/react-select/lib/Async'; import { TagOption } from './TagOption'; import { TagBadge } from './TagBadge'; -import IndicatorsContainer from 'app/core/components/Select/IndicatorsContainer'; -import NoOptionsMessage from 'app/core/components/Select/NoOptionsMessage'; import { components } from '@torkelo/react-select'; -import ResetStyles from 'app/core/components/Select/ResetStyles'; export interface Props { tags: string[]; @@ -51,7 +49,7 @@ export class TagFilter extends React.Component { getOptionValue: i => i.value, getOptionLabel: i => i.label, value: tags, - styles: ResetStyles, + styles: resetSelectStyles(), filterOption: (option, searchQuery) => { const regex = RegExp(searchQuery, 'i'); return regex.test(option.value); diff --git a/public/app/plugins/panel/gauge/MappingRow.tsx b/public/app/plugins/panel/gauge/MappingRow.tsx index 35d0b2e638c..277afb4fd5c 100644 --- a/public/app/plugins/panel/gauge/MappingRow.tsx +++ b/public/app/plugins/panel/gauge/MappingRow.tsx @@ -1,6 +1,6 @@ import React, { PureComponent } from 'react'; import { Label } from 'app/core/components/Label/Label'; -import { Select } from 'app/core/components/Select/Select'; +import { Select } from '@grafana/ui'; import { MappingType, RangeMap, ValueMap } from 'app/types'; interface Props { diff --git a/public/app/plugins/panel/gauge/ValueOptions.tsx b/public/app/plugins/panel/gauge/ValueOptions.tsx index 4aafc0b0457..e8af6bc2fe1 100644 --- a/public/app/plugins/panel/gauge/ValueOptions.tsx +++ b/public/app/plugins/panel/gauge/ValueOptions.tsx @@ -1,6 +1,6 @@ import React, { PureComponent } from 'react'; import { Label } from 'app/core/components/Label/Label'; -import Select from 'app/core/components/Select/Select'; +import { Select} from '@grafana/ui'; import UnitPicker from 'app/core/components/Select/UnitPicker'; import { PanelOptionsProps } from '@grafana/ui'; import { Options } from './types'; diff --git a/yarn.lock b/yarn.lock index 8eff64ca822..c5734ffcad3 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1098,14 +1098,14 @@ dependencies: "@types/react" "*" -"@types/react-transition-group@^2.0.15": +"@types/react-transition-group@*", "@types/react-transition-group@^2.0.15": version "2.0.15" resolved "https://registry.yarnpkg.com/@types/react-transition-group/-/react-transition-group-2.0.15.tgz#e5ee3fe558832e141cc6041bdd54caea7b787af8" integrity sha512-S0QnNzbHoWXDbKBl/xk5dxA4FT+BNlBcI3hku991cl8Cz3ytOkUMcCRtzdX11eb86E131bSsQqy5WrPCdJYblw== dependencies: "@types/react" "*" -"@types/react@*", "@types/react@^16.1.0", "@types/react@^16.7.6": +"@types/react@*", "@types/react@16.7.6", "@types/react@^16.1.0", "@types/react@^16.7.6": version "16.7.6" resolved "https://registry.yarnpkg.com/@types/react/-/react-16.7.6.tgz#80e4bab0d0731ad3ae51f320c4b08bdca5f03040" integrity sha512-QBUfzftr/8eg/q3ZRgf/GaDP6rTYc7ZNem+g4oZM38C9vXyV8AWRWaTQuW5yCoZTsfHrN7b3DeEiUnqH9SrnpA== @@ -3168,7 +3168,7 @@ caniuse-api@^1.5.2: lodash.memoize "^4.1.2" lodash.uniq "^4.5.0" -caniuse-db@^1.0.30000529, caniuse-db@^1.0.30000634, caniuse-db@^1.0.30000639: +caniuse-db@1.0.30000772, caniuse-db@^1.0.30000529, caniuse-db@^1.0.30000634, caniuse-db@^1.0.30000639: version "1.0.30000772" resolved "https://registry.yarnpkg.com/caniuse-db/-/caniuse-db-1.0.30000772.tgz#51aae891768286eade4a3d8319ea76d6a01b512b" integrity sha1-UarokXaChureSj2DGep21qAbUSs= From fbb3ad5fc45a93e040d1ab90092741612d990696 Mon Sep 17 00:00:00 2001 From: bergquist Date: Thu, 10 Jan 2019 09:35:48 +0100 Subject: [PATCH 57/91] make sure frequency cannot be zero frequency set to zero causes division by zero panics in the alert schedular. closes #14810 --- pkg/services/alerting/extractor.go | 2 +- pkg/services/alerting/rule.go | 21 ++++++-- pkg/services/alerting/rule_test.go | 80 ++++++++++++++++++++++-------- 3 files changed, 79 insertions(+), 24 deletions(-) diff --git a/pkg/services/alerting/extractor.go b/pkg/services/alerting/extractor.go index e33e3dc2af3..5b911c5a9ad 100644 --- a/pkg/services/alerting/extractor.go +++ b/pkg/services/alerting/extractor.go @@ -112,7 +112,7 @@ func (e *DashAlertExtractor) getAlertFromPanels(jsonWithPanels *simplejson.Json, frequency, err := getTimeDurationStringToSeconds(jsonAlert.Get("frequency").MustString()) if err != nil { - return nil, ValidationError{Reason: "Could not parse frequency"} + return nil, ValidationError{Reason: err.Error()} } rawFor := jsonAlert.Get("for").MustString() diff --git a/pkg/services/alerting/rule.go b/pkg/services/alerting/rule.go index d2a505145ac..4423046d600 100644 --- a/pkg/services/alerting/rule.go +++ b/pkg/services/alerting/rule.go @@ -1,16 +1,21 @@ package alerting import ( + "errors" "fmt" "regexp" "strconv" "time" "github.com/grafana/grafana/pkg/components/simplejson" - m "github.com/grafana/grafana/pkg/models" ) +var ( + ErrFrequencyCannotBeZeroOrLess = errors.New(`"evaluate every" cannot be zero or below`) + ErrFrequencyCouldNotBeParsed = errors.New(`"evaluate every" field could not be parsed`) +) + type Rule struct { Id int64 OrgId int64 @@ -76,7 +81,7 @@ func getTimeDurationStringToSeconds(str string) (int64, error) { matches := ValueFormatRegex.FindAllString(str, 1) if len(matches) <= 0 { - return 0, fmt.Errorf("Frequency could not be parsed") + return 0, ErrFrequencyCouldNotBeParsed } value, err := strconv.Atoi(matches[0]) @@ -84,6 +89,10 @@ func getTimeDurationStringToSeconds(str string) (int64, error) { return 0, err } + if value == 0 { + return 0, ErrFrequencyCannotBeZeroOrLess + } + unit := UnitFormatRegex.FindAllString(str, 1)[0] if val, ok := unitMultiplier[unit]; ok { @@ -101,7 +110,6 @@ func NewRuleFromDBAlert(ruleDef *m.Alert) (*Rule, error) { model.PanelId = ruleDef.PanelId model.Name = ruleDef.Name model.Message = ruleDef.Message - model.Frequency = ruleDef.Frequency model.State = ruleDef.State model.LastStateChange = ruleDef.NewStateDate model.For = ruleDef.For @@ -109,6 +117,13 @@ func NewRuleFromDBAlert(ruleDef *m.Alert) (*Rule, error) { model.ExecutionErrorState = m.ExecutionErrorOption(ruleDef.Settings.Get("executionErrorState").MustString("alerting")) model.StateChanges = ruleDef.StateChanges + model.Frequency = ruleDef.Frequency + // frequency cannot be zero since that would not execute the alert rule. + // so we fallback to 60 seconds if `Freqency` is missing + if model.Frequency == 0 { + model.Frequency = 60 + } + for _, v := range ruleDef.Settings.Get("notifications").MustArray() { jsonModel := simplejson.NewFromAny(v) id, err := jsonModel.Get("id").Int64() diff --git a/pkg/services/alerting/rule_test.go b/pkg/services/alerting/rule_test.go index 2a9e95e5723..cf25cc118f4 100644 --- a/pkg/services/alerting/rule_test.go +++ b/pkg/services/alerting/rule_test.go @@ -14,6 +14,36 @@ func (f *FakeCondition) Eval(context *EvalContext) (*ConditionResult, error) { return &ConditionResult{}, nil } +func TestAlertRuleFrequencyParsing(t *testing.T) { + tcs := []struct { + input string + err error + result int64 + }{ + {input: "10s", result: 10}, + {input: "10m", result: 600}, + {input: "1h", result: 3600}, + {input: "1o", result: 1}, + {input: "0s", err: ErrFrequencyCannotBeZeroOrLess}, + {input: "0m", err: ErrFrequencyCannotBeZeroOrLess}, + {input: "0h", err: ErrFrequencyCannotBeZeroOrLess}, + {input: "0", err: ErrFrequencyCannotBeZeroOrLess}, + {input: "-1s", err: ErrFrequencyCouldNotBeParsed}, + } + + for _, tc := range tcs { + r, err := getTimeDurationStringToSeconds(tc.input) + if err != tc.err { + t.Errorf("expected error: '%v' got: '%v'", tc.err, err) + return + } + + if r != tc.result { + t.Errorf("expected result: %d got %d", tc.result, r) + } + } +} + func TestAlertRuleModel(t *testing.T) { Convey("Testing alert rule", t, func() { @@ -21,26 +51,6 @@ func TestAlertRuleModel(t *testing.T) { return &FakeCondition{}, nil }) - Convey("Can parse seconds", func() { - seconds, _ := getTimeDurationStringToSeconds("10s") - So(seconds, ShouldEqual, 10) - }) - - Convey("Can parse minutes", func() { - seconds, _ := getTimeDurationStringToSeconds("10m") - So(seconds, ShouldEqual, 600) - }) - - Convey("Can parse hours", func() { - seconds, _ := getTimeDurationStringToSeconds("1h") - So(seconds, ShouldEqual, 3600) - }) - - Convey("defaults to seconds", func() { - seconds, _ := getTimeDurationStringToSeconds("1o") - So(seconds, ShouldEqual, 1) - }) - Convey("should return err for empty string", func() { _, err := getTimeDurationStringToSeconds("") So(err, ShouldNotBeNil) @@ -89,5 +99,35 @@ func TestAlertRuleModel(t *testing.T) { So(len(alertRule.Notifications), ShouldEqual, 2) }) }) + + Convey("can construct alert rule model with invalid frequency", func() { + json := ` + { + "name": "name2", + "description": "desc2", + "noDataMode": "critical", + "enabled": true, + "frequency": "0s", + "conditions": [ { "type": "test", "prop": 123 } ], + "notifications": [] + }` + + alertJSON, jsonErr := simplejson.NewJson([]byte(json)) + So(jsonErr, ShouldBeNil) + + alert := &m.Alert{ + Id: 1, + OrgId: 1, + DashboardId: 1, + PanelId: 1, + Frequency: 0, + + Settings: alertJSON, + } + + alertRule, err := NewRuleFromDBAlert(alert) + So(err, ShouldBeNil) + So(alertRule.Frequency, ShouldEqual, 60) + }) }) } From 53f0f08efab58384aac5f73c76c5de381f9f36db Mon Sep 17 00:00:00 2001 From: Dominik Prokop Date: Thu, 10 Jan 2019 14:24:24 +0100 Subject: [PATCH 58/91] Fixing TS and updating snapshot --- .../src/components/Select/IndicatorsContainer.tsx | 3 +++ .../grafana-ui/src/components/Select/NoOptionsMessage.tsx | 4 ++++ .../grafana-ui/src/components/Select/PickerOption.test.tsx | 2 -- packages/grafana-ui/src/components/Select/PickerOption.tsx | 3 +++ packages/grafana-ui/src/components/Select/Select.tsx | 5 +++++ .../Select/__snapshots__/PickerOption.test.tsx.snap | 7 ++++++- 6 files changed, 21 insertions(+), 3 deletions(-) rename {public/app/core => packages/grafana-ui/src}/components/Select/__snapshots__/PickerOption.test.tsx.snap (81%) diff --git a/packages/grafana-ui/src/components/Select/IndicatorsContainer.tsx b/packages/grafana-ui/src/components/Select/IndicatorsContainer.tsx index 8fc8e0b08d3..260fe6ebbdf 100644 --- a/packages/grafana-ui/src/components/Select/IndicatorsContainer.tsx +++ b/packages/grafana-ui/src/components/Select/IndicatorsContainer.tsx @@ -1,4 +1,7 @@ import React from 'react'; + +// Ignoring because I couldn't get @types/react-select work wih Torkel's fork +// @ts-ignore import { components } from '@torkelo/react-select'; export const IndicatorsContainer = (props: any) => { diff --git a/packages/grafana-ui/src/components/Select/NoOptionsMessage.tsx b/packages/grafana-ui/src/components/Select/NoOptionsMessage.tsx index 5fe229340a4..1cec06a5301 100644 --- a/packages/grafana-ui/src/components/Select/NoOptionsMessage.tsx +++ b/packages/grafana-ui/src/components/Select/NoOptionsMessage.tsx @@ -1,5 +1,9 @@ import React from 'react'; + +// Ignoring because I couldn't get @types/react-select work wih Torkel's fork +// @ts-ignore import { components } from '@torkelo/react-select'; +// @ts-ignore import { OptionProps } from '@torkelo/react-select/lib/components/Option'; export interface Props { diff --git a/packages/grafana-ui/src/components/Select/PickerOption.test.tsx b/packages/grafana-ui/src/components/Select/PickerOption.test.tsx index 96a4ff9cf2a..20187734c1a 100644 --- a/packages/grafana-ui/src/components/Select/PickerOption.test.tsx +++ b/packages/grafana-ui/src/components/Select/PickerOption.test.tsx @@ -38,9 +38,7 @@ describe('PickerOption', () => { ) diff --git a/packages/grafana-ui/src/components/Select/PickerOption.tsx b/packages/grafana-ui/src/components/Select/PickerOption.tsx index ac6a5c62783..4bbcb74a563 100644 --- a/packages/grafana-ui/src/components/Select/PickerOption.tsx +++ b/packages/grafana-ui/src/components/Select/PickerOption.tsx @@ -1,4 +1,7 @@ import React from 'react'; + +// Ignoring because I couldn't get @types/react-select work wih Torkel's fork +// @ts-ignore import { components } from '@torkelo/react-select'; import { OptionProps } from 'react-select/lib/components/Option'; diff --git a/packages/grafana-ui/src/components/Select/Select.tsx b/packages/grafana-ui/src/components/Select/Select.tsx index c456de1c94d..a2584ce8124 100644 --- a/packages/grafana-ui/src/components/Select/Select.tsx +++ b/packages/grafana-ui/src/components/Select/Select.tsx @@ -1,8 +1,13 @@ // Libraries import classNames from 'classnames'; import React, { PureComponent } from 'react'; + +// Ignoring because I couldn't get @types/react-select work wih Torkel's fork +// @ts-ignore import { default as ReactSelect } from '@torkelo/react-select'; +// @ts-ignore import { default as ReactAsyncSelect } from '@torkelo/react-select/lib/Async'; +// @ts-ignore import { components } from '@torkelo/react-select'; // Components diff --git a/public/app/core/components/Select/__snapshots__/PickerOption.test.tsx.snap b/packages/grafana-ui/src/components/Select/__snapshots__/PickerOption.test.tsx.snap similarity index 81% rename from public/app/core/components/Select/__snapshots__/PickerOption.test.tsx.snap rename to packages/grafana-ui/src/components/Select/__snapshots__/PickerOption.test.tsx.snap index 2136c22b1c6..c4185025a5d 100644 --- a/public/app/core/components/Select/__snapshots__/PickerOption.test.tsx.snap +++ b/packages/grafana-ui/src/components/Select/__snapshots__/PickerOption.test.tsx.snap @@ -1,7 +1,12 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`PickerOption renders correctly 1`] = ` -
+
From 702d4490018dcd41f763f81b7631fef17a873429 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20W=C3=BCrbach?= Date: Thu, 20 Dec 2018 22:48:53 +0100 Subject: [PATCH 59/91] Docker image for ARM --- .circleci/config.yml | 44 +++++++++++++------------- build.go | 2 ++ packaging/docker/Dockerfile | 6 ++-- packaging/docker/build.sh | 40 ++++++++++++++++++----- packaging/docker/push_to_docker_hub.sh | 36 +++++++++++++++++---- scripts/build/build.sh | 7 ++++ 6 files changed, 96 insertions(+), 39 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 236d5aec398..3d66a8ef13b 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -200,47 +200,47 @@ jobs: - dist/grafana* grafana-docker-master: - docker: - - image: docker:stable-git + machine: + image: circleci/classic:201808-01 steps: - checkout - attach_workspace: at: . - - setup_remote_docker - run: docker info - - run: cp dist/grafana-latest.linux-x64.tar.gz packaging/docker + - run: docker run --privileged linuxkit/binfmt:v0.6 + - run: cp dist/grafana-latest.linux-*.tar.gz packaging/docker - run: cd packaging/docker && ./build-deploy.sh "master-${CIRCLE_SHA1}" - - run: rm packaging/docker/grafana-latest.linux-x64.tar.gz + - run: rm packaging/docker/grafana-latest.linux-*.tar.gz - run: cp enterprise-dist/grafana-enterprise-*.linux-amd64.tar.gz packaging/docker/grafana-latest.linux-x64.tar.gz - run: cd packaging/docker && ./build-enterprise.sh "master" grafana-docker-pr: - docker: - - image: docker:stable-git + machine: + image: circleci/classic:201808-01 steps: - checkout - attach_workspace: at: . - - setup_remote_docker - run: docker info - - run: cp dist/grafana-latest.linux-x64.tar.gz packaging/docker + - run: docker run --privileged linuxkit/binfmt:v0.6 + - run: cp dist/grafana-latest.linux-*.tar.gz packaging/docker - run: cd packaging/docker && ./build.sh "${CIRCLE_SHA1}" grafana-docker-release: - docker: - - image: docker:stable-git - steps: - - checkout - - attach_workspace: - at: . - - setup_remote_docker - - run: docker info - - run: cp dist/grafana-latest.linux-x64.tar.gz packaging/docker - - run: cd packaging/docker && ./build-deploy.sh "${CIRCLE_TAG}" - - run: rm packaging/docker/grafana-latest.linux-x64.tar.gz - - run: cp enterprise-dist/grafana-enterprise-*.linux-amd64.tar.gz packaging/docker/grafana-latest.linux-x64.tar.gz - - run: cd packaging/docker && ./build-enterprise.sh "${CIRCLE_TAG}" + machine: + image: circleci/classic:201808-01 + steps: + - checkout + - attach_workspace: + at: . + - run: docker info + - run: docker run --privileged linuxkit/binfmt:v0.6 + - run: cp dist/grafana-latest.linux-*.tar.gz packaging/docker + - run: cd packaging/docker && ./build-deploy.sh "${CIRCLE_TAG}" + - run: rm packaging/docker/grafana-latest.linux-*.tar.gz + - run: cp enterprise-dist/grafana-enterprise-*.linux-amd64.tar.gz packaging/docker/grafana-latest.linux-x64.tar.gz + - run: cd packaging/docker && ./build-enterprise.sh "${CIRCLE_TAG}" build-enterprise: docker: diff --git a/build.go b/build.go index 9d5216de1d0..4486cd3deb9 100644 --- a/build.go +++ b/build.go @@ -164,6 +164,8 @@ func makeLatestDistCopies() { "_amd64.deb": "dist/grafana_latest_amd64.deb", ".x86_64.rpm": "dist/grafana-latest-1.x86_64.rpm", ".linux-amd64.tar.gz": "dist/grafana-latest.linux-x64.tar.gz", + ".linux-armv7.tar.gz": "dist/grafana-latest.linux-armv7.tar.gz", + ".linux-arm64.tar.gz": "dist/grafana-latest.linux-arm64.tar.gz", } for _, file := range files { diff --git a/packaging/docker/Dockerfile b/packaging/docker/Dockerfile index 4d4f6539972..d4f2f2aa7a3 100644 --- a/packaging/docker/Dockerfile +++ b/packaging/docker/Dockerfile @@ -1,4 +1,5 @@ -FROM debian:stretch-slim +ARG BASE_IMAGE=debian:stretch-slim +FROM ${BASE_IMAGE} ARG GRAFANA_TGZ="grafana-latest.linux-x64.tar.gz" @@ -10,7 +11,8 @@ COPY ${GRAFANA_TGZ} /tmp/grafana.tar.gz RUN mkdir /tmp/grafana && tar xfvz /tmp/grafana.tar.gz --strip-components=1 -C /tmp/grafana -FROM debian:stretch-slim +ARG BASE_IMAGE=debian:stretch-slim +FROM ${BASE_IMAGE} ARG GF_UID="472" ARG GF_GID="472" diff --git a/packaging/docker/build.sh b/packaging/docker/build.sh index c303c71cd5f..1bad2980d34 100755 --- a/packaging/docker/build.sh +++ b/packaging/docker/build.sh @@ -1,25 +1,49 @@ #!/bin/sh -_grafana_tag=$1 +_grafana_tag=${1:-} +_docker_repo=${2:-grafana/grafana} # If the tag starts with v, treat this as a official release if echo "$_grafana_tag" | grep -q "^v"; then _grafana_version=$(echo "${_grafana_tag}" | cut -d "v" -f 2) - _docker_repo=${2:-grafana/grafana} else _grafana_version=$_grafana_tag - _docker_repo=${2:-grafana/grafana-dev} fi echo "Building ${_docker_repo}:${_grafana_version}" -docker build \ - --tag "${_docker_repo}:${_grafana_version}" \ - --no-cache=true . +export DOCKER_CLI_EXPERIMENTAL=enabled + +# Build grafana image for a specific arch +docker_build () { + base_image=$1 + grafana_tgz=$2 + tag=$3 + + docker build \ + --build-arg BASE_IMAGE=${base_image} \ + --build-arg GRAFANA_TGZ=${grafana_tgz} \ + --tag "${tag}" \ + --no-cache=true . +} + +# Tag docker images of all architectures +docker_tag_all () { + repo=$1 + tag=$2 + docker tag "${_docker_repo}:${_grafana_version}" "${repo}:${tag}" + docker tag "${_docker_repo}-arm32v7-linux:${_grafana_version}" "${repo}-arm32v7-linux:${tag}" + docker tag "${_docker_repo}-arm64v8-linux:${_grafana_version}" "${repo}-arm64v8-linux:${tag}" +} + +docker_build "debian:stretch-slim" "grafana-latest.linux-x64.tar.gz" "${_docker_repo}:${_grafana_version}" +docker_build "arm32v7/debian:stretch-slim" "grafana-latest.linux-armv7.tar.gz" "${_docker_repo}-arm32v7-linux:${_grafana_version}" +docker_build "arm64v8/debian:stretch-slim" "grafana-latest.linux-arm64.tar.gz" "${_docker_repo}-arm64v8-linux:${_grafana_version}" # Tag as 'latest' for official release; otherwise tag as grafana/grafana:master if echo "$_grafana_tag" | grep -q "^v"; then - docker tag "${_docker_repo}:${_grafana_version}" "${_docker_repo}:latest" + docker_tag_all "${_docker_repo}" "latest" else - docker tag "${_docker_repo}:${_grafana_version}" "grafana/grafana:master" + docker_tag_all "${_docker_repo}" "master" + docker tag "${_docker_repo}:${_grafana_version} grafana/grafana-dev:${_grafana_version}" fi diff --git a/packaging/docker/push_to_docker_hub.sh b/packaging/docker/push_to_docker_hub.sh index 526c216f8fa..cef6d596851 100755 --- a/packaging/docker/push_to_docker_hub.sh +++ b/packaging/docker/push_to_docker_hub.sh @@ -1,24 +1,46 @@ #!/bin/sh set -e -_grafana_tag=$1 +_grafana_tag=${1:-} +_docker_repo=${2:-grafana/grafana} # If the tag starts with v, treat this as a official release if echo "$_grafana_tag" | grep -q "^v"; then _grafana_version=$(echo "${_grafana_tag}" | cut -d "v" -f 2) - _docker_repo=${2:-grafana/grafana} else _grafana_version=$_grafana_tag - _docker_repo=${2:-grafana/grafana-dev} fi +export DOCKER_CLI_EXPERIMENTAL=enabled + echo "pushing ${_docker_repo}:${_grafana_version}" -docker push "${_docker_repo}:${_grafana_version}" + + +docker_push_all () { + repo=$1 + tag=$2 + + # Push each image individually + docker push "${repo}:${tag}" + docker push "${repo}-arm32v7-linux:${tag}" + docker push "${repo}-arm64v8-linux:${tag}" + + # Create and push a multi-arch manifest + docker manifest create "${repo}:${tag}" \ + "${repo}:${tag}" \ + "${repo}-arm32v7-linux:${tag}" \ + "${repo}-arm64v8-linux:${tag}" + + docker manifest push "${repo}:${tag}" +} if echo "$_grafana_tag" | grep -q "^v" && echo "$_grafana_tag" | grep -vq "beta"; then echo "pushing ${_docker_repo}:latest" - docker push "${_docker_repo}:latest" + docker_push_all "${_docker_repo}" "latest" + docker_push_all "${_docker_repo}" "${_grafana_version}" +elif echo "$_grafana_tag" | grep -q "^v" && echo "$_grafana_tag" | grep -q "beta"; then + docker_push_all "${_docker_repo}" "${_grafana_version}" elif echo "$_grafana_tag" | grep -q "master"; then - echo "pushing grafana/grafana:master" - docker push grafana/grafana:master + docker_push_all "grafana/grafana" "master" + docker push "grafana/grafana-dev:${_grafana_version}" fi diff --git a/scripts/build/build.sh b/scripts/build/build.sh index 8362942c6cd..1222053f1c8 100755 --- a/scripts/build/build.sh +++ b/scripts/build/build.sh @@ -8,6 +8,8 @@ set -e EXTRA_OPTS="$@" +CCARMV7=arm-linux-gnueabihf-gcc +CCARM64=aarch64-linux-gnu-gcc CCX64=/tmp/x86_64-centos6-linux-gnu/bin/x86_64-centos6-linux-gnu-gcc GOPATH=/go @@ -26,6 +28,9 @@ fi echo "Build arguments: $OPT" +go run build.go -goarch armv7 -cc ${CCARMV7} ${OPT} build +go run build.go -goarch arm64 -cc ${CCARM64} ${OPT} build + CC=${CCX64} go run build.go ${OPT} build yarn install --pure-lockfile --no-progress @@ -44,3 +49,5 @@ source /etc/profile.d/rvm.sh echo "Packaging" go run build.go -goos linux -pkg-arch amd64 ${OPT} package-only latest +go run build.go -goos linux -pkg-arch armv7 ${OPT} package-only latest +go run build.go -goos linux -pkg-arch arm64 ${OPT} package-only latest From a82f0ed393ef14007b4333bcb83b01f8a58c8aeb Mon Sep 17 00:00:00 2001 From: Leonard Gram Date: Thu, 10 Jan 2019 13:46:03 +0100 Subject: [PATCH 60/91] build: tags arm as well as amd64 as latest. --- packaging/docker/build.sh | 2 +- packaging/docker/push_to_docker_hub.sh | 2 +- scripts/build/build-all.sh | 3 ++- scripts/build/build.sh | 8 +++++--- 4 files changed, 9 insertions(+), 6 deletions(-) diff --git a/packaging/docker/build.sh b/packaging/docker/build.sh index 1bad2980d34..a522363089b 100755 --- a/packaging/docker/build.sh +++ b/packaging/docker/build.sh @@ -45,5 +45,5 @@ if echo "$_grafana_tag" | grep -q "^v"; then docker_tag_all "${_docker_repo}" "latest" else docker_tag_all "${_docker_repo}" "master" - docker tag "${_docker_repo}:${_grafana_version} grafana/grafana-dev:${_grafana_version}" + docker tag "${_docker_repo}:${_grafana_version}" "grafana/grafana-dev:${_grafana_version}" fi diff --git a/packaging/docker/push_to_docker_hub.sh b/packaging/docker/push_to_docker_hub.sh index cef6d596851..37b5ae0095c 100755 --- a/packaging/docker/push_to_docker_hub.sh +++ b/packaging/docker/push_to_docker_hub.sh @@ -41,6 +41,6 @@ if echo "$_grafana_tag" | grep -q "^v" && echo "$_grafana_tag" | grep -vq "beta" elif echo "$_grafana_tag" | grep -q "^v" && echo "$_grafana_tag" | grep -q "beta"; then docker_push_all "${_docker_repo}" "${_grafana_version}" elif echo "$_grafana_tag" | grep -q "master"; then - docker_push_all "grafana/grafana" "master" + docker_push_all "${_docker_repo}" "master" docker push "grafana/grafana-dev:${_grafana_version}" fi diff --git a/scripts/build/build-all.sh b/scripts/build/build-all.sh index 3013452a279..980ef5cc4c2 100755 --- a/scripts/build/build-all.sh +++ b/scripts/build/build-all.sh @@ -59,7 +59,7 @@ go run build.go ${OPT} build-frontend source /etc/profile.d/rvm.sh echo "Packaging" -go run build.go -goos linux -pkg-arch amd64 ${OPT} package-only latest +go run build.go -goos linux -pkg-arch amd64 ${OPT} package-only #removing amd64 phantomjs bin for armv7/arm64 packages rm tools/phantomjs/phantomjs go run build.go -goos linux -pkg-arch armv7 ${OPT} package-only @@ -80,3 +80,4 @@ else fi go run build.go -goos windows -pkg-arch amd64 ${OPT} package-only +go run build.go latest \ No newline at end of file diff --git a/scripts/build/build.sh b/scripts/build/build.sh index 1222053f1c8..ac6aab0b867 100755 --- a/scripts/build/build.sh +++ b/scripts/build/build.sh @@ -48,6 +48,8 @@ go run build.go ${OPT} build-frontend source /etc/profile.d/rvm.sh echo "Packaging" -go run build.go -goos linux -pkg-arch amd64 ${OPT} package-only latest -go run build.go -goos linux -pkg-arch armv7 ${OPT} package-only latest -go run build.go -goos linux -pkg-arch arm64 ${OPT} package-only latest +go run build.go -goos linux -pkg-arch amd64 ${OPT} package-only +go run build.go -goos linux -pkg-arch armv7 ${OPT} package-only +go run build.go -goos linux -pkg-arch arm64 ${OPT} package-only + +go run build.go latest From c22ef628f32d5d7d29e44db634a48868d48d740b Mon Sep 17 00:00:00 2001 From: Leonard Gram Date: Thu, 10 Jan 2019 15:11:51 +0100 Subject: [PATCH 61/91] build: removes curl install from build. --- packaging/docker/build-deploy.sh | 1 - 1 file changed, 1 deletion(-) diff --git a/packaging/docker/build-deploy.sh b/packaging/docker/build-deploy.sh index ac3226a4a61..22655bead8c 100755 --- a/packaging/docker/build-deploy.sh +++ b/packaging/docker/build-deploy.sh @@ -8,6 +8,5 @@ docker login -u "$DOCKER_USER" -p "$DOCKER_PASS" ./push_to_docker_hub.sh "$_grafana_version" if echo "$_grafana_version" | grep -q "^master-"; then - apk add --no-cache curl ./deploy_to_k8s.sh "grafana/grafana-dev:$_grafana_version" fi From d322717f3ee8551fffe11d883aff8446241a1ff6 Mon Sep 17 00:00:00 2001 From: Dominik Prokop Date: Thu, 10 Jan 2019 15:21:11 +0100 Subject: [PATCH 62/91] Renamed Select related components: Picker* to Select*, Option* to SelectOption* --- packages/grafana-ui/src/components/Select/Select.tsx | 8 ++++---- .../{PickerOption.test.tsx => SelectOption.test.tsx} | 6 +++--- .../Select/{PickerOption.tsx => SelectOption.tsx} | 4 ++-- .../Select/{OptionGroup.tsx => SelectOptionGroup.tsx} | 2 +- ...kerOption.test.tsx.snap => SelectOption.test.tsx.snap} | 2 +- 5 files changed, 11 insertions(+), 11 deletions(-) rename packages/grafana-ui/src/components/Select/{PickerOption.test.tsx => SelectOption.test.tsx} (90%) rename packages/grafana-ui/src/components/Select/{PickerOption.tsx => SelectOption.tsx} (93%) rename packages/grafana-ui/src/components/Select/{OptionGroup.tsx => SelectOptionGroup.tsx} (93%) rename packages/grafana-ui/src/components/Select/__snapshots__/{PickerOption.test.tsx.snap => SelectOption.test.tsx.snap} (90%) diff --git a/packages/grafana-ui/src/components/Select/Select.tsx b/packages/grafana-ui/src/components/Select/Select.tsx index a2584ce8124..b3b0c8efbbb 100644 --- a/packages/grafana-ui/src/components/Select/Select.tsx +++ b/packages/grafana-ui/src/components/Select/Select.tsx @@ -11,8 +11,8 @@ import { default as ReactAsyncSelect } from '@torkelo/react-select/lib/Async'; import { components } from '@torkelo/react-select'; // Components -import { Option, SingleValue } from './PickerOption'; -import OptionGroup from './OptionGroup'; +import { SelectOption, SingleValue } from './SelectOption'; +import SelectOptionGroup from './SelectOptionGroup'; import IndicatorsContainer from './IndicatorsContainer'; import NoOptionsMessage from './NoOptionsMessage'; import resetSelectStyles from './resetSelectStyles'; @@ -117,11 +117,11 @@ export class Select extends PureComponent { classNamePrefix="gf-form-select-box" className={selectClassNames} components={{ - Option, + Option: SelectOption, SingleValue, IndicatorsContainer, MenuList, - Group: OptionGroup, + Group: SelectOptionGroup, }} defaultValue={defaultValue} value={value} diff --git a/packages/grafana-ui/src/components/Select/PickerOption.test.tsx b/packages/grafana-ui/src/components/Select/SelectOption.test.tsx similarity index 90% rename from packages/grafana-ui/src/components/Select/PickerOption.test.tsx rename to packages/grafana-ui/src/components/Select/SelectOption.test.tsx index 20187734c1a..a7326b3f4db 100644 --- a/packages/grafana-ui/src/components/Select/PickerOption.test.tsx +++ b/packages/grafana-ui/src/components/Select/SelectOption.test.tsx @@ -1,6 +1,6 @@ import React from 'react'; import renderer from 'react-test-renderer'; -import PickerOption from './PickerOption'; +import SelectOption from './SelectOption'; import { OptionProps } from 'react-select/lib/components/Option'; const model: OptionProps = { @@ -31,11 +31,11 @@ const model: OptionProps = { className: 'class-for-user-picker', }; -describe('PickerOption', () => { +describe('SelectOption', () => { it('renders correctly', () => { const tree = renderer .create( - { }; } -export const Option = (props: ExtendedOptionProps) => { +export const SelectOption = (props: ExtendedOptionProps) => { const { children, isSelected, data } = props; return ( @@ -44,4 +44,4 @@ export const SingleValue = (props: any) => { ); }; -export default Option; +export default SelectOption; diff --git a/packages/grafana-ui/src/components/Select/OptionGroup.tsx b/packages/grafana-ui/src/components/Select/SelectOptionGroup.tsx similarity index 93% rename from packages/grafana-ui/src/components/Select/OptionGroup.tsx rename to packages/grafana-ui/src/components/Select/SelectOptionGroup.tsx index ed2b72a537c..30842f02e29 100644 --- a/packages/grafana-ui/src/components/Select/OptionGroup.tsx +++ b/packages/grafana-ui/src/components/Select/SelectOptionGroup.tsx @@ -9,7 +9,7 @@ interface State { expanded: boolean; } -export default class OptionGroup extends PureComponent { +export default class SelectOptionGroup extends PureComponent { state = { expanded: false, }; diff --git a/packages/grafana-ui/src/components/Select/__snapshots__/PickerOption.test.tsx.snap b/packages/grafana-ui/src/components/Select/__snapshots__/SelectOption.test.tsx.snap similarity index 90% rename from packages/grafana-ui/src/components/Select/__snapshots__/PickerOption.test.tsx.snap rename to packages/grafana-ui/src/components/Select/__snapshots__/SelectOption.test.tsx.snap index c4185025a5d..c52be902edd 100644 --- a/packages/grafana-ui/src/components/Select/__snapshots__/PickerOption.test.tsx.snap +++ b/packages/grafana-ui/src/components/Select/__snapshots__/SelectOption.test.tsx.snap @@ -1,6 +1,6 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`PickerOption renders correctly 1`] = ` +exports[`SelectOption renders correctly 1`] = `
Date: Thu, 10 Jan 2019 16:50:36 +0100 Subject: [PATCH 63/91] changelog: docker images for arm. --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 671740f7225..46b7381cba1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,7 @@ * **OAuth**: Support OAuth providers that are not RFC6749 compliant [#14562](https://github.com/grafana/grafana/issues/14562), thx [@tdabasinskas](https://github.com/tdabasinskas) * **Units**: Add blood glucose level units mg/dL and mmol/L [#14519](https://github.com/grafana/grafana/issues/14519), thx [@kjedamzik](https://github.com/kjedamzik) * **Stackdriver**: Aggregating series returns more than one series [#14581](https://github.com/grafana/grafana/issues/14581) and [#13914](https://github.com/grafana/grafana/issues/13914), thx [@kinok](https://github.com/kinok) +* **Docker**: Build and publish docker images for armv7 and arm64 [#14617](https://github.com/grafana/grafana/pull/14617), thx [@johanneswuerbach](https://github.com/johanneswuerbach) ### Bug fixes * **Search**: Fix for issue with scrolling the "tags filter" dropdown, fixes [#14486](https://github.com/grafana/grafana/issues/14486) From 0f82fffed65449299394c109903f1ccac6d268d7 Mon Sep 17 00:00:00 2001 From: Leonard Gram Date: Thu, 10 Jan 2019 15:22:30 +0100 Subject: [PATCH 64/91] build: makes sure all builds use the latest container. --- .circleci/config.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 3d66a8ef13b..7f9c40bd968 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -127,7 +127,7 @@ jobs: build-all: docker: - - image: grafana/build-container:1.2.1 + - image: grafana/build-container:1.2.2 working_directory: /go/src/github.com/grafana/grafana steps: - checkout @@ -244,7 +244,7 @@ jobs: build-enterprise: docker: - - image: grafana/build-container:1.2.1 + - image: grafana/build-container:1.2.2 working_directory: /go/src/github.com/grafana/grafana steps: - checkout @@ -276,7 +276,7 @@ jobs: build-all-enterprise: docker: - - image: grafana/build-container:1.2.1 + - image: grafana/build-container:1.2.2 working_directory: /go/src/github.com/grafana/grafana steps: - checkout From 60fadcf1e583b1243a2a0334389210a9b7a2807a Mon Sep 17 00:00:00 2001 From: Dominik Prokop Date: Thu, 10 Jan 2019 20:15:03 +0100 Subject: [PATCH 65/91] Fix panel time overrides not being applied fully When both relative and time shift were applied, only time shift was taken into consideration --- public/app/features/dashboard/utils/panel.ts | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/public/app/features/dashboard/utils/panel.ts b/public/app/features/dashboard/utils/panel.ts index f7ed0efd910..cf00a31c71e 100644 --- a/public/app/features/dashboard/utils/panel.ts +++ b/public/app/features/dashboard/utils/panel.ts @@ -143,12 +143,9 @@ export function applyPanelTimeOverrides(panel: PanelModel, timeRange: TimeRange) const timeShift = '-' + timeShiftInterpolated; newTimeData.timeInfo += ' timeshift ' + timeShift; newTimeData.timeRange = { - from: dateMath.parseDateMath(timeShift, timeRange.from, false), - to: dateMath.parseDateMath(timeShift, timeRange.to, true), - raw: { - from: timeRange.from, - to: timeRange.to, - }, + from: dateMath.parseDateMath(timeShift, newTimeData.timeRange.from, false), + to: dateMath.parseDateMath(timeShift, newTimeData.timeRange.to, true), + raw: newTimeData.timeRange.raw, }; } From 08ac2959a4d17a58aaf1f64738d1e442c6de93eb Mon Sep 17 00:00:00 2001 From: Peter Holmberg Date: Thu, 10 Jan 2019 21:47:09 +0000 Subject: [PATCH 66/91] Moving to grafana ui, fix issue with TestRuleResult --- .../LoadingPlaceholder/LoadingPlaceholder.tsx | 11 ++++++++++ packages/grafana-ui/src/components/index.ts | 1 + public/app/features/alerting/AlertTab.tsx | 21 ++++++------------- .../features/alerting/TestRuleButton.test.tsx | 6 +++--- ...{TestRuleButton.tsx => TestRuleResult.tsx} | 6 ++++-- .../dashboard/dashgrid/QueriesTab.tsx | 11 +++------- 6 files changed, 28 insertions(+), 28 deletions(-) create mode 100644 packages/grafana-ui/src/components/LoadingPlaceholder/LoadingPlaceholder.tsx rename public/app/features/alerting/{TestRuleButton.tsx => TestRuleResult.tsx} (86%) diff --git a/packages/grafana-ui/src/components/LoadingPlaceholder/LoadingPlaceholder.tsx b/packages/grafana-ui/src/components/LoadingPlaceholder/LoadingPlaceholder.tsx new file mode 100644 index 00000000000..01048014f8a --- /dev/null +++ b/packages/grafana-ui/src/components/LoadingPlaceholder/LoadingPlaceholder.tsx @@ -0,0 +1,11 @@ +import React, { SFC } from 'react'; + +interface LoadingPlaceholderProps { + text: string; +} + +export const LoadingPlaceholder: SFC = ({ text }) => ( +
+ {text} +
+); diff --git a/packages/grafana-ui/src/components/index.ts b/packages/grafana-ui/src/components/index.ts index abb1cf1b34c..6fa7de62572 100644 --- a/packages/grafana-ui/src/components/index.ts +++ b/packages/grafana-ui/src/components/index.ts @@ -2,3 +2,4 @@ export { DeleteButton } from './DeleteButton/DeleteButton'; export { Tooltip } from './Tooltip/Tooltip'; export { Portal } from './Portal/Portal'; export { CustomScrollbar } from './CustomScrollbar/CustomScrollbar'; +export { LoadingPlaceholder } from './LoadingPlaceholder/LoadingPlaceholder'; diff --git a/public/app/features/alerting/AlertTab.tsx b/public/app/features/alerting/AlertTab.tsx index 5623fac95c1..0520cd5e6e8 100644 --- a/public/app/features/alerting/AlertTab.tsx +++ b/public/app/features/alerting/AlertTab.tsx @@ -1,11 +1,12 @@ // Libraries -import React, { PureComponent, SFC } from 'react'; +import React, { PureComponent } from 'react'; // Services & Utils import { AngularComponent, getAngularLoader } from 'app/core/services/AngularLoader'; import appEvents from 'app/core/app_events'; // Components +import { LoadingPlaceholder } from '@grafana/ui'; import { EditorTabBody, EditorToolbarView } from '../dashboard/dashgrid/EditorTabBody'; import EmptyListCTA from 'app/core/components/EmptyListCTA/EmptyListCTA'; import StateHistory from './StateHistory'; @@ -14,7 +15,7 @@ import 'app/features/alerting/AlertTabCtrl'; // Types import { DashboardModel } from '../dashboard/dashboard_model'; import { PanelModel } from '../dashboard/panel_model'; -import { TestRuleButton } from './TestRuleButton'; +import { TestRuleResult } from './TestRuleResult'; interface Props { angularPanel?: AngularComponent; @@ -22,16 +23,6 @@ interface Props { panel: PanelModel; } -interface LoadingPlaceholderProps { - text: string; -} - -const LoadingPlaceholder: SFC = ({ text }) => ( -
- {text} -
-); - export class AlertTab extends PureComponent { element: any; component: AngularComponent; @@ -120,14 +111,14 @@ export class AlertTab extends PureComponent { }; }; - renderTestRuleButton = () => { + renderTestRuleResult = () => { const { panel, dashboard } = this.props; - return ; + return ; }; testRule = (): EditorToolbarView => ({ title: 'Test Rule', - render: () => this.renderTestRuleButton(), + render: () => this.renderTestRuleResult(), }); onAddAlert = () => { diff --git a/public/app/features/alerting/TestRuleButton.test.tsx b/public/app/features/alerting/TestRuleButton.test.tsx index ae3b570cf43..b762ebf2579 100644 --- a/public/app/features/alerting/TestRuleButton.test.tsx +++ b/public/app/features/alerting/TestRuleButton.test.tsx @@ -1,7 +1,7 @@ import React from 'react'; import { shallow } from 'enzyme'; import { DashboardModel } from '../dashboard/dashboard_model'; -import { Props, TestRuleButton } from './TestRuleButton'; +import { Props, TestRuleResult } from './TestRuleResult'; jest.mock('app/core/services/backend_srv', () => ({ getBackendSrv: () => ({ @@ -18,9 +18,9 @@ const setup = (propOverrides?: object) => { Object.assign(props, propOverrides); - const wrapper = shallow(); + const wrapper = shallow(); - return { wrapper, instance: wrapper.instance() as TestRuleButton }; + return { wrapper, instance: wrapper.instance() as TestRuleResult }; }; describe('Render', () => { diff --git a/public/app/features/alerting/TestRuleButton.tsx b/public/app/features/alerting/TestRuleResult.tsx similarity index 86% rename from public/app/features/alerting/TestRuleButton.tsx rename to public/app/features/alerting/TestRuleResult.tsx index f9927b1a182..e55dd6aae51 100644 --- a/public/app/features/alerting/TestRuleButton.tsx +++ b/public/app/features/alerting/TestRuleResult.tsx @@ -14,7 +14,7 @@ interface State { testRuleResponse: {}; } -export class TestRuleButton extends PureComponent { +export class TestRuleResult extends PureComponent { readonly state: State = { isLoading: false, testRuleResponse: {}, @@ -27,8 +27,10 @@ export class TestRuleButton extends PureComponent { async testRule() { const { panelId, dashboard } = this.props; const payload = { dashboard: dashboard.getSaveModelClone(), panelId }; + + this.setState({ isLoading: true }); const testRuleResponse = await getBackendSrv().post(`/api/alerts/test`, payload); - this.setState(prevState => ({ ...prevState, isLoading: false, testRuleResponse })); + this.setState({ isLoading: false, testRuleResponse }); } render() { diff --git a/public/app/features/dashboard/dashgrid/QueriesTab.tsx b/public/app/features/dashboard/dashgrid/QueriesTab.tsx index 77ab64b1dba..eab7a95d471 100644 --- a/public/app/features/dashboard/dashgrid/QueriesTab.tsx +++ b/public/app/features/dashboard/dashgrid/QueriesTab.tsx @@ -1,15 +1,16 @@ // Libraries -import React, { PureComponent, SFC } from 'react'; +import React, { PureComponent } from 'react'; import _ from 'lodash'; // Components import 'app/features/panel/metrics_tab'; -import { EditorTabBody, EditorToolbarView} from './EditorTabBody'; +import { EditorTabBody, EditorToolbarView } from './EditorTabBody'; 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 { LoadingPlaceholder } from '@grafana/ui'; // Services import { getDatasourceSrv } from 'app/features/plugins/datasource_srv'; @@ -36,12 +37,6 @@ interface State { isAddingMixed: boolean; } -interface LoadingPlaceholderProps { - text: string; -} - -const LoadingPlaceholder: SFC = ({ text }) =>

{text}

; - export class QueriesTab extends PureComponent { element: HTMLElement; component: AngularComponent; From 2f0ab99ae5e63bc58984b6cc126228962ea38b21 Mon Sep 17 00:00:00 2001 From: Peter Holmberg Date: Thu, 10 Jan 2019 22:15:37 +0000 Subject: [PATCH 67/91] Fixing test and small refactor --- public/app/features/alerting/AlertTab.tsx | 3 +-- ...tRuleButton.test.tsx => TestRuleResult.test.tsx} | 1 - public/app/features/alerting/TestRuleResult.tsx | 3 +-- .../__snapshots__/TestRuleButton.test.tsx.snap | 13 ------------- .../__snapshots__/TestRuleResult.test.tsx.snap | 7 +++++++ .../app/features/dashboard/dashgrid/QueriesTab.tsx | 3 +-- .../features/dashboard/dashgrid/QueryInspector.tsx | 3 +-- 7 files changed, 11 insertions(+), 22 deletions(-) rename public/app/features/alerting/{TestRuleButton.test.tsx => TestRuleResult.test.tsx} (97%) delete mode 100644 public/app/features/alerting/__snapshots__/TestRuleButton.test.tsx.snap create mode 100644 public/app/features/alerting/__snapshots__/TestRuleResult.test.tsx.snap diff --git a/public/app/features/alerting/AlertTab.tsx b/public/app/features/alerting/AlertTab.tsx index 0520cd5e6e8..2a1b3d12ecf 100644 --- a/public/app/features/alerting/AlertTab.tsx +++ b/public/app/features/alerting/AlertTab.tsx @@ -6,7 +6,6 @@ import { AngularComponent, getAngularLoader } from 'app/core/services/AngularLoa import appEvents from 'app/core/app_events'; // Components -import { LoadingPlaceholder } from '@grafana/ui'; import { EditorTabBody, EditorToolbarView } from '../dashboard/dashgrid/EditorTabBody'; import EmptyListCTA from 'app/core/components/EmptyListCTA/EmptyListCTA'; import StateHistory from './StateHistory'; @@ -113,7 +112,7 @@ export class AlertTab extends PureComponent { renderTestRuleResult = () => { const { panel, dashboard } = this.props; - return ; + return ; }; testRule = (): EditorToolbarView => ({ diff --git a/public/app/features/alerting/TestRuleButton.test.tsx b/public/app/features/alerting/TestRuleResult.test.tsx similarity index 97% rename from public/app/features/alerting/TestRuleButton.test.tsx rename to public/app/features/alerting/TestRuleResult.test.tsx index b762ebf2579..9beb5ade632 100644 --- a/public/app/features/alerting/TestRuleButton.test.tsx +++ b/public/app/features/alerting/TestRuleResult.test.tsx @@ -13,7 +13,6 @@ const setup = (propOverrides?: object) => { const props: Props = { panelId: 1, dashboard: new DashboardModel({ panels: [{ id: 1 }] }), - LoadingPlaceholder: {}, }; Object.assign(props, propOverrides); diff --git a/public/app/features/alerting/TestRuleResult.tsx b/public/app/features/alerting/TestRuleResult.tsx index e55dd6aae51..4014e529597 100644 --- a/public/app/features/alerting/TestRuleResult.tsx +++ b/public/app/features/alerting/TestRuleResult.tsx @@ -2,11 +2,11 @@ import React, { PureComponent } from 'react'; import { JSONFormatter } from 'app/core/components/JSONFormatter/JSONFormatter'; import { getBackendSrv } from 'app/core/services/backend_srv'; import { DashboardModel } from '../dashboard/dashboard_model'; +import { LoadingPlaceholder } from '@grafana/ui/src'; export interface Props { panelId: number; dashboard: DashboardModel; - LoadingPlaceholder: any; } interface State { @@ -35,7 +35,6 @@ export class TestRuleResult extends PureComponent { render() { const { testRuleResponse, isLoading } = this.state; - const { LoadingPlaceholder } = this.props; if (isLoading === true) { return ; diff --git a/public/app/features/alerting/__snapshots__/TestRuleButton.test.tsx.snap b/public/app/features/alerting/__snapshots__/TestRuleButton.test.tsx.snap deleted file mode 100644 index d1ed3e64e99..00000000000 --- a/public/app/features/alerting/__snapshots__/TestRuleButton.test.tsx.snap +++ /dev/null @@ -1,13 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`Render should render component 1`] = ` - -`; diff --git a/public/app/features/alerting/__snapshots__/TestRuleResult.test.tsx.snap b/public/app/features/alerting/__snapshots__/TestRuleResult.test.tsx.snap new file mode 100644 index 00000000000..73f85f12354 --- /dev/null +++ b/public/app/features/alerting/__snapshots__/TestRuleResult.test.tsx.snap @@ -0,0 +1,7 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Render should render component 1`] = ` + +`; diff --git a/public/app/features/dashboard/dashgrid/QueriesTab.tsx b/public/app/features/dashboard/dashgrid/QueriesTab.tsx index eab7a95d471..a20f8627fba 100644 --- a/public/app/features/dashboard/dashgrid/QueriesTab.tsx +++ b/public/app/features/dashboard/dashgrid/QueriesTab.tsx @@ -10,7 +10,6 @@ import { QueryInspector } from './QueryInspector'; import { QueryOptions } from './QueryOptions'; import { AngularQueryComponentScope } from 'app/features/panel/metrics_tab'; import { PanelOptionSection } from './PanelOptionSection'; -import { LoadingPlaceholder } from '@grafana/ui'; // Services import { getDatasourceSrv } from 'app/features/plugins/datasource_srv'; @@ -129,7 +128,7 @@ export class QueriesTab extends PureComponent { renderQueryInspector = () => { const { panel } = this.props; - return ; + return ; }; renderHelp = () => { diff --git a/public/app/features/dashboard/dashgrid/QueryInspector.tsx b/public/app/features/dashboard/dashgrid/QueryInspector.tsx index 090bc220bc0..8e490f6b622 100644 --- a/public/app/features/dashboard/dashgrid/QueryInspector.tsx +++ b/public/app/features/dashboard/dashgrid/QueryInspector.tsx @@ -2,6 +2,7 @@ import React, { PureComponent } from 'react'; import { JSONFormatter } from 'app/core/components/JSONFormatter/JSONFormatter'; import appEvents from 'app/core/app_events'; import { CopyToClipboard } from 'app/core/components/CopyToClipboard/CopyToClipboard'; +import { LoadingPlaceholder } from '@grafana/ui'; interface DsQuery { isLoading: boolean; @@ -10,7 +11,6 @@ interface DsQuery { interface Props { panel: any; - LoadingPlaceholder: any; } interface State { @@ -177,7 +177,6 @@ export class QueryInspector extends PureComponent { render() { const { response, isLoading } = this.state.dsQuery; - const { LoadingPlaceholder } = this.props; const { isMocking } = this.state; const openNodes = this.getNrOfOpenNodes(); From d376fae393b099811af036e799738880a2e542ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hugo=20H=C3=A4ggmark?= Date: Thu, 10 Jan 2019 13:34:23 +0100 Subject: [PATCH 68/91] Moved colorpicker to ui/components --- packages/grafana-ui/package.json | 4 +- .../ColorPicker}/ColorPalette.test.tsx | 2 +- .../components/ColorPicker}/ColorPalette.tsx | 6 +- .../components/ColorPicker}/ColorPicker.tsx | 10 +- .../ColorPicker}/ColorPickerPopover.tsx | 33 +++---- .../ColorPicker}/SeriesColorPicker.tsx | 4 +- .../ColorPicker}/SeriesColorPickerPopover.tsx | 11 +-- .../ColorPicker}/SpectrumPicker.tsx | 8 +- .../__snapshots__/ColorPalette.test.tsx.snap | 0 packages/grafana-ui/src/components/index.ts | 3 + packages/grafana-ui/src/index.ts | 1 + packages/grafana-ui/src/utils/colors.ts | 94 +++++++++++++++++++ packages/grafana-ui/src/utils/index.ts | 2 + public/app/core/angular_wrappers.ts | 10 ++ public/app/core/core.ts | 4 +- public/app/core/logs_model.ts | 4 +- public/app/core/utils/colors.ts | 94 ------------------- public/app/core/utils/explore.ts | 2 +- .../app/features/annotations/event_manager.ts | 7 +- .../app/features/dashboard/dashboard_model.ts | 2 +- public/app/plugins/panel/gauge/Thresholds.tsx | 2 +- .../panel/graph/Legend/LegendSeriesItem.tsx | 2 +- .../app/plugins/panel/graph/data_processor.ts | 3 +- .../app/plugins/panel/graph2/GraphPanel.tsx | 2 +- public/app/routes/GrafanaCtrl.ts | 4 +- public/app/viz/state/timeSeries.ts | 2 +- 26 files changed, 157 insertions(+), 159 deletions(-) rename {public/app/core/specs => packages/grafana-ui/src/components/ColorPicker}/ColorPalette.test.tsx (80%) rename {public/app/core/components/colorpicker => packages/grafana-ui/src/components/ColorPicker}/ColorPalette.tsx (90%) rename {public/app/core/components/colorpicker => packages/grafana-ui/src/components/ColorPicker}/ColorPicker.tsx (85%) rename {public/app/core/components/colorpicker => packages/grafana-ui/src/components/ColorPicker}/ColorPickerPopover.tsx (83%) rename {public/app/core/components/colorpicker => packages/grafana-ui/src/components/ColorPicker}/SeriesColorPicker.tsx (96%) rename {public/app/core/components/colorpicker => packages/grafana-ui/src/components/ColorPicker}/SeriesColorPickerPopover.tsx (86%) rename {public/app/core/components/colorpicker => packages/grafana-ui/src/components/ColorPicker}/SpectrumPicker.tsx (92%) rename {public/app/core/specs => packages/grafana-ui/src/components/ColorPicker}/__snapshots__/ColorPalette.test.tsx.snap (100%) create mode 100644 packages/grafana-ui/src/utils/colors.ts diff --git a/packages/grafana-ui/package.json b/packages/grafana-ui/package.json index 6548f75e91f..5221e1ba02f 100644 --- a/packages/grafana-ui/package.json +++ b/packages/grafana-ui/package.json @@ -23,7 +23,9 @@ "react-highlight-words": "0.11.0", "react-popper": "^1.3.0", "react-transition-group": "^2.2.1", - "react-virtualized": "^9.21.0" + "react-virtualized": "^9.21.0", + "tether-drop": "https://github.com/torkelo/drop/tarball/master", + "tinycolor2": "^1.4.1" }, "devDependencies": { "@types/classnames": "^2.2.6", diff --git a/public/app/core/specs/ColorPalette.test.tsx b/packages/grafana-ui/src/components/ColorPicker/ColorPalette.test.tsx similarity index 80% rename from public/app/core/specs/ColorPalette.test.tsx rename to packages/grafana-ui/src/components/ColorPicker/ColorPalette.test.tsx index fb1124aa975..0714180de54 100644 --- a/public/app/core/specs/ColorPalette.test.tsx +++ b/packages/grafana-ui/src/components/ColorPicker/ColorPalette.test.tsx @@ -1,6 +1,6 @@ import React from 'react'; import renderer from 'react-test-renderer'; -import { ColorPalette } from '../components/colorpicker/ColorPalette'; +import { ColorPalette } from './ColorPalette'; describe('CollorPalette', () => { it('renders correctly', () => { diff --git a/public/app/core/components/colorpicker/ColorPalette.tsx b/packages/grafana-ui/src/components/ColorPicker/ColorPalette.tsx similarity index 90% rename from public/app/core/components/colorpicker/ColorPalette.tsx rename to packages/grafana-ui/src/components/ColorPicker/ColorPalette.tsx index edb2629d16d..03ed9949361 100644 --- a/public/app/core/components/colorpicker/ColorPalette.tsx +++ b/packages/grafana-ui/src/components/ColorPicker/ColorPalette.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import { sortedColors } from 'app/core/utils/colors'; +import { sortedColors } from '../../utils'; export interface Props { color: string; @@ -9,13 +9,13 @@ export interface Props { export class ColorPalette extends React.Component { paletteColors: string[]; - constructor(props) { + constructor(props: Props) { super(props); this.paletteColors = sortedColors; this.onColorSelect = this.onColorSelect.bind(this); } - onColorSelect(color) { + onColorSelect(color: string) { return () => { this.props.onColorSelect(color); }; diff --git a/public/app/core/components/colorpicker/ColorPicker.tsx b/packages/grafana-ui/src/components/ColorPicker/ColorPicker.tsx similarity index 85% rename from public/app/core/components/colorpicker/ColorPicker.tsx rename to packages/grafana-ui/src/components/ColorPicker/ColorPicker.tsx index 9541001b0a8..fbe14d4eb8c 100644 --- a/public/app/core/components/colorpicker/ColorPicker.tsx +++ b/packages/grafana-ui/src/components/ColorPicker/ColorPicker.tsx @@ -2,7 +2,6 @@ import React from 'react'; import ReactDOM from 'react-dom'; import Drop from 'tether-drop'; import { ColorPickerPopover } from './ColorPickerPopover'; -import { react2AngularDirective } from 'app/core/utils/react2angular'; export interface Props { color: string; @@ -10,7 +9,7 @@ export interface Props { } export class ColorPicker extends React.Component { - pickerElem: HTMLElement; + pickerElem: HTMLElement | null; colorPickerDrop: any; openColorPicker = () => { @@ -45,7 +44,7 @@ export class ColorPicker extends React.Component { }, 100); }; - onColorSelect = color => { + onColorSelect = (color: string) => { this.props.onChange(color); }; @@ -59,8 +58,3 @@ export class ColorPicker extends React.Component { ); } } - -react2AngularDirective('colorPicker', ColorPicker, [ - 'color', - ['onChange', { watchDepth: 'reference', wrapApply: true }], -]); diff --git a/public/app/core/components/colorpicker/ColorPickerPopover.tsx b/packages/grafana-ui/src/components/ColorPicker/ColorPickerPopover.tsx similarity index 83% rename from public/app/core/components/colorpicker/ColorPickerPopover.tsx rename to packages/grafana-ui/src/components/ColorPicker/ColorPickerPopover.tsx index c42bcfa1d06..e8305c99319 100644 --- a/public/app/core/components/colorpicker/ColorPickerPopover.tsx +++ b/packages/grafana-ui/src/components/ColorPicker/ColorPickerPopover.tsx @@ -14,7 +14,7 @@ export interface Props { export class ColorPickerPopover extends React.Component { pickerNavElem: any; - constructor(props) { + constructor(props: Props) { super(props); this.state = { tab: 'palette', @@ -23,60 +23,51 @@ export class ColorPickerPopover extends React.Component { }; } - setPickerNavElem(elem) { + setPickerNavElem(elem: any) { this.pickerNavElem = $(elem); } - setColor(color) { + setColor(color: string) { const newColor = tinycolor(color); if (newColor.isValid()) { - this.setState({ - color: newColor.toString(), - colorString: newColor.toString(), - }); + this.setState({ color: newColor.toString(), colorString: newColor.toString() }); this.props.onColorSelect(color); } } - sampleColorSelected(color) { + sampleColorSelected(color: string) { this.setColor(color); } - spectrumColorSelected(color) { + spectrumColorSelected(color: any) { const rgbColor = color.toRgbString(); this.setColor(rgbColor); } - onColorStringChange(e) { + onColorStringChange(e: any) { const colorString = e.target.value; - this.setState({ - colorString: colorString, - }); + this.setState({ colorString: colorString }); const newColor = tinycolor(colorString); if (newColor.isValid()) { // Update only color state const newColorString = newColor.toString(); - this.setState({ - color: newColorString, - }); + this.setState({ color: newColorString }); this.props.onColorSelect(newColorString); } } - onColorStringBlur(e) { + onColorStringBlur(e: any) { const colorString = e.target.value; this.setColor(colorString); } componentDidMount() { this.pickerNavElem.find('li:first').addClass('active'); - this.pickerNavElem.on('show', e => { + this.pickerNavElem.on('show', (e: any) => { // use href attr (#name => name) const tab = e.target.hash.slice(1); - this.setState({ - tab: tab, - }); + this.setState({ tab: tab }); }); } diff --git a/public/app/core/components/colorpicker/SeriesColorPicker.tsx b/packages/grafana-ui/src/components/ColorPicker/SeriesColorPicker.tsx similarity index 96% rename from public/app/core/components/colorpicker/SeriesColorPicker.tsx rename to packages/grafana-ui/src/components/ColorPicker/SeriesColorPicker.tsx index 32b7554e38d..b8ba03b7feb 100644 --- a/public/app/core/components/colorpicker/SeriesColorPicker.tsx +++ b/packages/grafana-ui/src/components/ColorPicker/SeriesColorPicker.tsx @@ -8,7 +8,7 @@ export interface SeriesColorPickerProps { yaxis?: number; optionalClass?: string; onColorChange: (newColor: string) => void; - onToggleAxis?: () => void; + onToggleAxis: () => void; } export class SeriesColorPicker extends React.Component { @@ -21,7 +21,7 @@ export class SeriesColorPicker extends React.Component { onToggleAxis: () => {}, }; - constructor(props) { + constructor(props: SeriesColorPickerProps) { super(props); } diff --git a/public/app/core/components/colorpicker/SeriesColorPickerPopover.tsx b/packages/grafana-ui/src/components/ColorPicker/SeriesColorPickerPopover.tsx similarity index 86% rename from public/app/core/components/colorpicker/SeriesColorPickerPopover.tsx rename to packages/grafana-ui/src/components/ColorPicker/SeriesColorPickerPopover.tsx index 085d554300d..9036a1a2ffd 100644 --- a/public/app/core/components/colorpicker/SeriesColorPickerPopover.tsx +++ b/packages/grafana-ui/src/components/ColorPicker/SeriesColorPickerPopover.tsx @@ -1,12 +1,11 @@ import React from 'react'; import { ColorPickerPopover } from './ColorPickerPopover'; -import { react2AngularDirective } from 'app/core/utils/react2angular'; export interface SeriesColorPickerPopoverProps { color: string; yaxis?: number; onColorChange: (color: string) => void; - onToggleAxis?: () => void; + onToggleAxis: () => void; } export class SeriesColorPickerPopover extends React.PureComponent { @@ -30,7 +29,7 @@ interface AxisSelectorState { } export class AxisSelector extends React.PureComponent { - constructor(props) { + constructor(props: AxisSelectorProps) { super(props); this.state = { yaxis: this.props.yaxis, @@ -62,9 +61,3 @@ export class AxisSelector extends React.PureComponent { elem: any; isMoving: boolean; - constructor(props) { + constructor(props: Props) { super(props); this.onSpectrumMove = this.onSpectrumMove.bind(this); this.setComponentElem = this.setComponentElem.bind(this); } - setComponentElem(elem) { + setComponentElem(elem: any) { this.elem = $(elem); } - onSpectrumMove(color) { + onSpectrumMove(color: any) { this.isMoving = true; this.props.onColorSelect(color); } @@ -46,7 +46,7 @@ export class SpectrumPicker extends React.Component { this.elem.spectrum('set', this.props.color); } - componentWillUpdate(nextProps) { + componentWillUpdate(nextProps: any) { // If user move pointer over spectrum field this produce 'move' event and component // may update props.color. We don't want to update spectrum color in this case, so we can use // isMoving flag for tracking moving state. Flag should be cleared in componentDidUpdate() which diff --git a/public/app/core/specs/__snapshots__/ColorPalette.test.tsx.snap b/packages/grafana-ui/src/components/ColorPicker/__snapshots__/ColorPalette.test.tsx.snap similarity index 100% rename from public/app/core/specs/__snapshots__/ColorPalette.test.tsx.snap rename to packages/grafana-ui/src/components/ColorPicker/__snapshots__/ColorPalette.test.tsx.snap diff --git a/packages/grafana-ui/src/components/index.ts b/packages/grafana-ui/src/components/index.ts index abb1cf1b34c..b2b607415b8 100644 --- a/packages/grafana-ui/src/components/index.ts +++ b/packages/grafana-ui/src/components/index.ts @@ -2,3 +2,6 @@ export { DeleteButton } from './DeleteButton/DeleteButton'; export { Tooltip } from './Tooltip/Tooltip'; export { Portal } from './Portal/Portal'; export { CustomScrollbar } from './CustomScrollbar/CustomScrollbar'; +export { ColorPicker } from './ColorPicker/ColorPicker'; +export { SeriesColorPickerPopover } from './ColorPicker/SeriesColorPickerPopover'; +export { SeriesColorPicker } from './ColorPicker/SeriesColorPicker'; diff --git a/packages/grafana-ui/src/index.ts b/packages/grafana-ui/src/index.ts index b22152497b9..4072052a07d 100644 --- a/packages/grafana-ui/src/index.ts +++ b/packages/grafana-ui/src/index.ts @@ -2,4 +2,5 @@ export * from './components'; export * from './visualizations'; export * from './types'; export * from './utils'; +export { default } from './utils'; export * from './forms'; diff --git a/packages/grafana-ui/src/utils/colors.ts b/packages/grafana-ui/src/utils/colors.ts new file mode 100644 index 00000000000..673b0109f11 --- /dev/null +++ b/packages/grafana-ui/src/utils/colors.ts @@ -0,0 +1,94 @@ +import _ from 'lodash'; +import tinycolor from 'tinycolor2'; + +export const PALETTE_ROWS = 4; +export const PALETTE_COLUMNS = 14; +export const DEFAULT_ANNOTATION_COLOR = 'rgba(0, 211, 255, 1)'; +export const OK_COLOR = 'rgba(11, 237, 50, 1)'; +export const ALERTING_COLOR = 'rgba(237, 46, 24, 1)'; +export const NO_DATA_COLOR = 'rgba(150, 150, 150, 1)'; +export const PENDING_COLOR = 'rgba(247, 149, 32, 1)'; +export const REGION_FILL_ALPHA = 0.09; + +const colors = [ + '#7EB26D', // 0: pale green + '#EAB839', // 1: mustard + '#6ED0E0', // 2: light blue + '#EF843C', // 3: orange + '#E24D42', // 4: red + '#1F78C1', // 5: ocean + '#BA43A9', // 6: purple + '#705DA0', // 7: violet + '#508642', // 8: dark green + '#CCA300', // 9: dark sand + '#447EBC', + '#C15C17', + '#890F02', + '#0A437C', + '#6D1F62', + '#584477', + '#B7DBAB', + '#F4D598', + '#70DBED', + '#F9BA8F', + '#F29191', + '#82B5D8', + '#E5A8E2', + '#AEA2E0', + '#629E51', + '#E5AC0E', + '#64B0C8', + '#E0752D', + '#BF1B00', + '#0A50A1', + '#962D82', + '#614D93', + '#9AC48A', + '#F2C96D', + '#65C5DB', + '#F9934E', + '#EA6460', + '#5195CE', + '#D683CE', + '#806EB7', + '#3F6833', + '#967302', + '#2F575E', + '#99440A', + '#58140C', + '#052B51', + '#511749', + '#3F2B5B', + '#E0F9D7', + '#FCEACA', + '#CFFAFF', + '#F9E2D2', + '#FCE2DE', + '#BADFF4', + '#F9D9F9', + '#DEDAF7', +]; + +function sortColorsByHue(hexColors: string[]) { + const hslColors = _.map(hexColors, hexToHsl); + + let sortedHSLColors = _.sortBy(hslColors, ['h']); + sortedHSLColors = _.chunk(sortedHSLColors, PALETTE_ROWS); + sortedHSLColors = _.map(sortedHSLColors, chunk => { + return _.sortBy(chunk, 'l'); + }); + sortedHSLColors = _.flattenDeep(_.zip(...sortedHSLColors)); + + return _.map(sortedHSLColors, hslToHex); +} + +function hexToHsl(color: string) { + return tinycolor(color).toHsl(); +} + +function hslToHex(color: string) { + return tinycolor(color).toHexString(); +} + +export let sortedColors = sortColorsByHue(colors); +export default colors; diff --git a/packages/grafana-ui/src/utils/index.ts b/packages/grafana-ui/src/utils/index.ts index 4d9b9a4b948..1a677b1f033 100644 --- a/packages/grafana-ui/src/utils/index.ts +++ b/packages/grafana-ui/src/utils/index.ts @@ -1 +1,3 @@ export * from './processTimeSeries'; +export * from './colors'; +export { default } from './colors'; diff --git a/public/app/core/angular_wrappers.ts b/public/app/core/angular_wrappers.ts index 5609c058a27..d6fc68293c3 100644 --- a/public/app/core/angular_wrappers.ts +++ b/public/app/core/angular_wrappers.ts @@ -6,6 +6,7 @@ import { SearchResult } from './components/search/SearchResult'; import { TagFilter } from './components/TagFilter/TagFilter'; import { SideMenu } from './components/sidemenu/SideMenu'; import AppNotificationList from './components/AppNotifications/AppNotificationList'; +import { ColorPicker, SeriesColorPickerPopover } from '@grafana/ui'; export function registerAngularDirectives() { react2AngularDirective('passwordStrength', PasswordStrength, ['password']); @@ -19,4 +20,13 @@ export function registerAngularDirectives() { ['onChange', { watchDepth: 'reference' }], ['tagOptions', { watchDepth: 'reference' }], ]); + react2AngularDirective('colorPicker', ColorPicker, [ + 'color', + ['onChange', { watchDepth: 'reference', wrapApply: true }], + ]); + react2AngularDirective('seriesColorPickerPopover', SeriesColorPickerPopover, [ + 'series', + 'onColorChange', + 'onToggleAxis', + ]); } diff --git a/public/app/core/core.ts b/public/app/core/core.ts index 257a2077c97..19e9a473e35 100644 --- a/public/app/core/core.ts +++ b/public/app/core/core.ts @@ -13,8 +13,6 @@ import './partials'; import './components/jsontree/jsontree'; import './components/code_editor/code_editor'; import './utils/outline'; -import './components/colorpicker/ColorPicker'; -import './components/colorpicker/SeriesColorPickerPopover'; import './components/colorpicker/spectrum_picker'; import './services/search_srv'; import './services/ng_react'; @@ -36,7 +34,7 @@ import 'app/core/services/all'; import './filters/filters'; import coreModule from './core_module'; import appEvents from './app_events'; -import colors from './utils/colors'; +import colors from '@grafana/ui/'; import { assignModelProperties } from './utils/model_utils'; import { contextSrv } from './services/context_srv'; import { KeybindingSrv } from './services/keybindingSrv'; diff --git a/public/app/core/logs_model.ts b/public/app/core/logs_model.ts index 4e8c6207959..8eb8433cd53 100644 --- a/public/app/core/logs_model.ts +++ b/public/app/core/logs_model.ts @@ -1,6 +1,8 @@ import _ from 'lodash'; +import colors from '@grafana/ui'; + import { TimeSeries } from 'app/core/core'; -import colors, { getThemeColor } from 'app/core/utils/colors'; +import { getThemeColor } from 'app/core/utils/colors'; /** * Mapping of log level abbreviation to canonical log level. diff --git a/public/app/core/utils/colors.ts b/public/app/core/utils/colors.ts index 34508e94a9f..6d73ab9fbd8 100644 --- a/public/app/core/utils/colors.ts +++ b/public/app/core/utils/colors.ts @@ -1,99 +1,5 @@ -import _ from 'lodash'; -import tinycolor from 'tinycolor2'; import config from 'app/core/config'; -export const PALETTE_ROWS = 4; -export const PALETTE_COLUMNS = 14; -export const DEFAULT_ANNOTATION_COLOR = 'rgba(0, 211, 255, 1)'; -export const OK_COLOR = 'rgba(11, 237, 50, 1)'; -export const ALERTING_COLOR = 'rgba(237, 46, 24, 1)'; -export const NO_DATA_COLOR = 'rgba(150, 150, 150, 1)'; -export const PENDING_COLOR = 'rgba(247, 149, 32, 1)'; -export const REGION_FILL_ALPHA = 0.09; - -const colors = [ - '#7EB26D', // 0: pale green - '#EAB839', // 1: mustard - '#6ED0E0', // 2: light blue - '#EF843C', // 3: orange - '#E24D42', // 4: red - '#1F78C1', // 5: ocean - '#BA43A9', // 6: purple - '#705DA0', // 7: violet - '#508642', // 8: dark green - '#CCA300', // 9: dark sand - '#447EBC', - '#C15C17', - '#890F02', - '#0A437C', - '#6D1F62', - '#584477', - '#B7DBAB', - '#F4D598', - '#70DBED', - '#F9BA8F', - '#F29191', - '#82B5D8', - '#E5A8E2', - '#AEA2E0', - '#629E51', - '#E5AC0E', - '#64B0C8', - '#E0752D', - '#BF1B00', - '#0A50A1', - '#962D82', - '#614D93', - '#9AC48A', - '#F2C96D', - '#65C5DB', - '#F9934E', - '#EA6460', - '#5195CE', - '#D683CE', - '#806EB7', - '#3F6833', - '#967302', - '#2F575E', - '#99440A', - '#58140C', - '#052B51', - '#511749', - '#3F2B5B', - '#E0F9D7', - '#FCEACA', - '#CFFAFF', - '#F9E2D2', - '#FCE2DE', - '#BADFF4', - '#F9D9F9', - '#DEDAF7', -]; - -export function sortColorsByHue(hexColors) { - const hslColors = _.map(hexColors, hexToHsl); - - let sortedHSLColors = _.sortBy(hslColors, ['h']); - sortedHSLColors = _.chunk(sortedHSLColors, PALETTE_ROWS); - sortedHSLColors = _.map(sortedHSLColors, chunk => { - return _.sortBy(chunk, 'l'); - }); - sortedHSLColors = _.flattenDeep(_.zip(...sortedHSLColors)); - - return _.map(sortedHSLColors, hslToHex); -} - -export function hexToHsl(color) { - return tinycolor(color).toHsl(); -} - -export function hslToHex(color) { - return tinycolor(color).toHexString(); -} - export function getThemeColor(dark: string, light: string): string { return config.bootData.user.lightTheme ? light : dark; } - -export let sortedColors = sortColorsByHue(colors); -export default colors; diff --git a/public/app/core/utils/explore.ts b/public/app/core/utils/explore.ts index bea166075dc..2cca33620b7 100644 --- a/public/app/core/utils/explore.ts +++ b/public/app/core/utils/explore.ts @@ -1,9 +1,9 @@ import _ from 'lodash'; +import colors from '@grafana/ui'; import { renderUrl } from 'app/core/utils/url'; import kbn from 'app/core/utils/kbn'; import store from 'app/core/store'; -import colors from 'app/core/utils/colors'; import { parse as parseDate } from 'app/core/utils/datemath'; import TimeSeries from 'app/core/time_series2'; diff --git a/public/app/features/annotations/event_manager.ts b/public/app/features/annotations/event_manager.ts index db748e639a1..6966d3cdc82 100644 --- a/public/app/features/annotations/event_manager.ts +++ b/public/app/features/annotations/event_manager.ts @@ -1,8 +1,6 @@ import _ from 'lodash'; import moment from 'moment'; import tinycolor from 'tinycolor2'; -import { MetricsPanelCtrl } from 'app/plugins/sdk'; -import { AnnotationEvent } from './event'; import { OK_COLOR, ALERTING_COLOR, @@ -10,7 +8,10 @@ import { PENDING_COLOR, DEFAULT_ANNOTATION_COLOR, REGION_FILL_ALPHA, -} from 'app/core/utils/colors'; +} from '@grafana/ui'; + +import { MetricsPanelCtrl } from 'app/plugins/sdk'; +import { AnnotationEvent } from './event'; export class EventManager { event: AnnotationEvent; diff --git a/public/app/features/dashboard/dashboard_model.ts b/public/app/features/dashboard/dashboard_model.ts index 6f98bc5a17a..747ea9fecaa 100644 --- a/public/app/features/dashboard/dashboard_model.ts +++ b/public/app/features/dashboard/dashboard_model.ts @@ -1,8 +1,8 @@ import moment from 'moment'; import _ from 'lodash'; +import { DEFAULT_ANNOTATION_COLOR } from '@grafana/ui'; import { GRID_COLUMN_COUNT, REPEAT_DIR_VERTICAL, GRID_CELL_HEIGHT, GRID_CELL_VMARGIN } from 'app/core/constants'; -import { DEFAULT_ANNOTATION_COLOR } from 'app/core/utils/colors'; import { Emitter } from 'app/core/utils/emitter'; import { contextSrv } from 'app/core/services/context_srv'; import sortByKeys from 'app/core/utils/sort_by_keys'; diff --git a/public/app/plugins/panel/gauge/Thresholds.tsx b/public/app/plugins/panel/gauge/Thresholds.tsx index b4d4930e11d..7699a499146 100644 --- a/public/app/plugins/panel/gauge/Thresholds.tsx +++ b/public/app/plugins/panel/gauge/Thresholds.tsx @@ -1,6 +1,6 @@ import React, { PureComponent } from 'react'; import tinycolor from 'tinycolor2'; -import { ColorPicker } from 'app/core/components/colorpicker/ColorPicker'; +import { ColorPicker } from '@grafana/ui'; import { BasicGaugeColor, Threshold } from 'app/types'; import { PanelOptionsProps } from '@grafana/ui'; import { Options } from './types'; diff --git a/public/app/plugins/panel/graph/Legend/LegendSeriesItem.tsx b/public/app/plugins/panel/graph/Legend/LegendSeriesItem.tsx index 2105687d8e1..d6df17d9699 100644 --- a/public/app/plugins/panel/graph/Legend/LegendSeriesItem.tsx +++ b/public/app/plugins/panel/graph/Legend/LegendSeriesItem.tsx @@ -1,7 +1,7 @@ import React, { PureComponent } from 'react'; import classNames from 'classnames'; import { TimeSeries } from 'app/core/core'; -import { SeriesColorPicker } from 'app/core/components/colorpicker/SeriesColorPicker'; +import { SeriesColorPicker } from '@grafana/ui'; export const LEGEND_STATS = ['min', 'max', 'avg', 'current', 'total']; diff --git a/public/app/plugins/panel/graph/data_processor.ts b/public/app/plugins/panel/graph/data_processor.ts index 4ea1efe1502..ef4ad872753 100644 --- a/public/app/plugins/panel/graph/data_processor.ts +++ b/public/app/plugins/panel/graph/data_processor.ts @@ -1,6 +1,7 @@ import _ from 'lodash'; +import colors from '@grafana/ui'; + import TimeSeries from 'app/core/time_series2'; -import colors from 'app/core/utils/colors'; export class DataProcessor { constructor(private panel) {} diff --git a/public/app/plugins/panel/graph2/GraphPanel.tsx b/public/app/plugins/panel/graph2/GraphPanel.tsx index 020c33f7d38..11be172aaeb 100644 --- a/public/app/plugins/panel/graph2/GraphPanel.tsx +++ b/public/app/plugins/panel/graph2/GraphPanel.tsx @@ -1,7 +1,7 @@ // Libraries import _ from 'lodash'; import React, { PureComponent } from 'react'; -import colors from 'app/core/utils/colors'; +import colors from '@grafana/ui'; // Components & Types import { Graph, PanelProps, NullValueMode, processTimeSeries } from '@grafana/ui'; diff --git a/public/app/routes/GrafanaCtrl.ts b/public/app/routes/GrafanaCtrl.ts index 75a34ac01c0..434a112692e 100644 --- a/public/app/routes/GrafanaCtrl.ts +++ b/public/app/routes/GrafanaCtrl.ts @@ -1,12 +1,12 @@ import config from 'app/core/config'; import _ from 'lodash'; import $ from 'jquery'; +import Drop from 'tether-drop'; +import colors from '@grafana/ui'; import coreModule from 'app/core/core_module'; import { profiler } from 'app/core/profiler'; import appEvents from 'app/core/app_events'; -import Drop from 'tether-drop'; -import colors from 'app/core/utils/colors'; import { BackendSrv, setBackendSrv } from 'app/core/services/backend_srv'; import { TimeSrv, setTimeSrv } from 'app/features/dashboard/time_srv'; import { DatasourceSrv, setDatasourceSrv } from 'app/features/plugins/datasource_srv'; diff --git a/public/app/viz/state/timeSeries.ts b/public/app/viz/state/timeSeries.ts index 782383957bc..2329d6f41af 100644 --- a/public/app/viz/state/timeSeries.ts +++ b/public/app/viz/state/timeSeries.ts @@ -2,7 +2,7 @@ import _ from 'lodash'; // Utils -import colors from 'app/core/utils/colors'; +import colors from '@grafana/ui'; // Types import { TimeSeries, TimeSeriesVMs, NullValueMode } from '@grafana/ui'; From 37dae043d703c9586a9573ae3de1d463d150e4e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hugo=20H=C3=A4ggmark?= Date: Thu, 10 Jan 2019 13:38:55 +0100 Subject: [PATCH 69/91] Small change in SeriesColorPickerPopoverProps --- .../src/components/ColorPicker/SeriesColorPicker.tsx | 2 +- .../components/ColorPicker/SeriesColorPickerPopover.tsx | 9 ++++++--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/packages/grafana-ui/src/components/ColorPicker/SeriesColorPicker.tsx b/packages/grafana-ui/src/components/ColorPicker/SeriesColorPicker.tsx index b8ba03b7feb..09a53a8fe60 100644 --- a/packages/grafana-ui/src/components/ColorPicker/SeriesColorPicker.tsx +++ b/packages/grafana-ui/src/components/ColorPicker/SeriesColorPicker.tsx @@ -8,7 +8,7 @@ export interface SeriesColorPickerProps { yaxis?: number; optionalClass?: string; onColorChange: (newColor: string) => void; - onToggleAxis: () => void; + onToggleAxis?: () => void; } export class SeriesColorPicker extends React.Component { diff --git a/packages/grafana-ui/src/components/ColorPicker/SeriesColorPickerPopover.tsx b/packages/grafana-ui/src/components/ColorPicker/SeriesColorPickerPopover.tsx index 9036a1a2ffd..541a77ddabc 100644 --- a/packages/grafana-ui/src/components/ColorPicker/SeriesColorPickerPopover.tsx +++ b/packages/grafana-ui/src/components/ColorPicker/SeriesColorPickerPopover.tsx @@ -5,7 +5,7 @@ export interface SeriesColorPickerPopoverProps { color: string; yaxis?: number; onColorChange: (color: string) => void; - onToggleAxis: () => void; + onToggleAxis?: () => void; } export class SeriesColorPickerPopover extends React.PureComponent { @@ -21,7 +21,7 @@ export class SeriesColorPickerPopover extends React.PureComponent void; + onToggleAxis?: () => void; } interface AxisSelectorState { @@ -41,7 +41,10 @@ export class AxisSelector extends React.PureComponent Date: Thu, 10 Jan 2019 16:52:08 +0100 Subject: [PATCH 70/91] Fixed typings --- packages/grafana-ui/package.json | 3 +++ .../components/ColorPicker/ColorPicker.tsx | 3 ++- .../ColorPicker/SeriesColorPicker.tsx | 1 + packages/grafana-ui/src/utils/colors.ts | 12 ++++++------ yarn.lock | 19 ++++++++++++++++++- 5 files changed, 30 insertions(+), 8 deletions(-) diff --git a/packages/grafana-ui/package.json b/packages/grafana-ui/package.json index 5221e1ba02f..91695dc5647 100644 --- a/packages/grafana-ui/package.json +++ b/packages/grafana-ui/package.json @@ -24,6 +24,7 @@ "react-popper": "^1.3.0", "react-transition-group": "^2.2.1", "react-virtualized": "^9.21.0", + "tether": "^1.4.0", "tether-drop": "https://github.com/torkelo/drop/tarball/master", "tinycolor2": "^1.4.1" }, @@ -35,6 +36,8 @@ "@types/react": "^16.7.6", "@types/react-custom-scrollbars": "^4.0.5", "@types/react-test-renderer": "^16.0.3", + "@types/tether-drop": "^1.4.8", + "@types/tinycolor2": "^1.4.1", "react-test-renderer": "^16.7.0", "typescript": "^3.2.2" } diff --git a/packages/grafana-ui/src/components/ColorPicker/ColorPicker.tsx b/packages/grafana-ui/src/components/ColorPicker/ColorPicker.tsx index fbe14d4eb8c..485aa5f03d3 100644 --- a/packages/grafana-ui/src/components/ColorPicker/ColorPicker.tsx +++ b/packages/grafana-ui/src/components/ColorPicker/ColorPicker.tsx @@ -19,7 +19,7 @@ export class ColorPicker extends React.Component { ReactDOM.render(dropContent, dropContentElem); const drop = new Drop({ - target: this.pickerElem, + target: this.pickerElem as Element, content: dropContentElem, position: 'top center', classes: 'drop-popover', @@ -27,6 +27,7 @@ export class ColorPicker extends React.Component { hoverCloseDelay: 200, tetherOptions: { constraints: [{ to: 'scrollParent', attachment: 'none both' }], + attachment: 'bottom center', }, }); diff --git a/packages/grafana-ui/src/components/ColorPicker/SeriesColorPicker.tsx b/packages/grafana-ui/src/components/ColorPicker/SeriesColorPicker.tsx index 09a53a8fe60..7c3848f6868 100644 --- a/packages/grafana-ui/src/components/ColorPicker/SeriesColorPicker.tsx +++ b/packages/grafana-ui/src/components/ColorPicker/SeriesColorPicker.tsx @@ -51,6 +51,7 @@ export class SeriesColorPicker extends React.Component { remove: true, tetherOptions: { constraints: [{ to: 'scrollParent', attachment: 'none both' }], + attachment: 'bottom center', }, }); diff --git a/packages/grafana-ui/src/utils/colors.ts b/packages/grafana-ui/src/utils/colors.ts index 673b0109f11..0b22cd7f81d 100644 --- a/packages/grafana-ui/src/utils/colors.ts +++ b/packages/grafana-ui/src/utils/colors.ts @@ -72,21 +72,21 @@ const colors = [ function sortColorsByHue(hexColors: string[]) { const hslColors = _.map(hexColors, hexToHsl); - let sortedHSLColors = _.sortBy(hslColors, ['h']); - sortedHSLColors = _.chunk(sortedHSLColors, PALETTE_ROWS); - sortedHSLColors = _.map(sortedHSLColors, chunk => { + const sortedHSLColors = _.sortBy(hslColors, ['h']); + const chunkedHSLColors = _.chunk(sortedHSLColors, PALETTE_ROWS); + const sortedChunkedHSLColors = _.map(chunkedHSLColors, chunk => { return _.sortBy(chunk, 'l'); }); - sortedHSLColors = _.flattenDeep(_.zip(...sortedHSLColors)); + const flattenedZippedSortedChunkedHSLColors = _.flattenDeep(_.zip(...sortedChunkedHSLColors)); - return _.map(sortedHSLColors, hslToHex); + return _.map(flattenedZippedSortedChunkedHSLColors, hslToHex); } function hexToHsl(color: string) { return tinycolor(color).toHsl(); } -function hslToHex(color: string) { +function hslToHex(color: any) { return tinycolor(color).toHexString(); } diff --git a/yarn.lock b/yarn.lock index 8eff64ca822..d6342e8dc26 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1098,7 +1098,7 @@ dependencies: "@types/react" "*" -"@types/react-transition-group@^2.0.15": +"@types/react-transition-group@*", "@types/react-transition-group@^2.0.15": version "2.0.15" resolved "https://registry.yarnpkg.com/@types/react-transition-group/-/react-transition-group-2.0.15.tgz#e5ee3fe558832e141cc6041bdd54caea7b787af8" integrity sha512-S0QnNzbHoWXDbKBl/xk5dxA4FT+BNlBcI3hku991cl8Cz3ytOkUMcCRtzdX11eb86E131bSsQqy5WrPCdJYblw== @@ -1118,6 +1118,23 @@ resolved "https://registry.yarnpkg.com/@types/tapable/-/tapable-0.2.5.tgz#2443fc12da514c81346b1a665675559cee21fa75" integrity sha512-dEoVvo/I9QFomyhY+4Q6Qk+I+dhG59TYceZgC6Q0mCifVPErx6Y83PNTKGDS5e9h9Eti6q0S2mm16BU6iQK+3w== +"@types/tether-drop@^1.4.8": + version "1.4.8" + resolved "https://registry.yarnpkg.com/@types/tether-drop/-/tether-drop-1.4.8.tgz#8d64288e673259d1bc28518250b80b5ef43af0bc" + integrity sha512-QzrJDUxnLoqACUm7opxGOwa9mgMBlkyb7hHYWApMLM3ywWif4pWraTiotooiG3ePZmnTe8wQj2nx7GWMX4pb+w== + dependencies: + "@types/tether" "*" + +"@types/tether@*": + version "1.4.4" + resolved "https://registry.yarnpkg.com/@types/tether/-/tether-1.4.4.tgz#0fde1ccbd2f1fad74f8f465fe6227ff3b7bff634" + integrity sha512-6qhsFJVMuMqaQRVyQVi3zUBLfKYyryktL0ZP0Z3zegzeQ7WKm0PZNCdl3JsaitJbzqaoQ9qsFKMfaj5MiMfcSQ== + +"@types/tinycolor2@^1.4.1": + version "1.4.1" + resolved "https://registry.yarnpkg.com/@types/tinycolor2/-/tinycolor2-1.4.1.tgz#2f5670c9d1d6e558897a810ed284b44918fc1253" + integrity sha512-25L/RL5tqZkquKXVHM1fM2bd23qjfbcPpAZ2N/H05Y45g3UEi+Hw8CbDV28shKY8gH1SHiLpZSxPI1lacqdpGg== + "@types/uglify-js@*": version "3.0.3" resolved "https://registry.yarnpkg.com/@types/uglify-js/-/uglify-js-3.0.3.tgz#801a5ca1dc642861f47c46d14b700ed2d610840b" From dc9b83030f5ae162b005404abdb9da3d13d7b27f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hugo=20H=C3=A4ggmark?= Date: Fri, 11 Jan 2019 06:48:17 +0100 Subject: [PATCH 71/91] Removed default export for colors --- packages/grafana-ui/src/index.ts | 1 - packages/grafana-ui/src/utils/colors.ts | 3 +-- packages/grafana-ui/src/utils/index.ts | 1 - public/app/core/core.ts | 2 +- public/app/core/logs_model.ts | 2 +- public/app/core/utils/explore.ts | 2 +- public/app/plugins/panel/graph/data_processor.ts | 2 +- public/app/plugins/panel/graph2/GraphPanel.tsx | 2 +- public/app/routes/GrafanaCtrl.ts | 2 +- public/app/viz/state/timeSeries.ts | 2 +- 10 files changed, 8 insertions(+), 11 deletions(-) diff --git a/packages/grafana-ui/src/index.ts b/packages/grafana-ui/src/index.ts index 4072052a07d..b22152497b9 100644 --- a/packages/grafana-ui/src/index.ts +++ b/packages/grafana-ui/src/index.ts @@ -2,5 +2,4 @@ export * from './components'; export * from './visualizations'; export * from './types'; export * from './utils'; -export { default } from './utils'; export * from './forms'; diff --git a/packages/grafana-ui/src/utils/colors.ts b/packages/grafana-ui/src/utils/colors.ts index 0b22cd7f81d..263d128aec4 100644 --- a/packages/grafana-ui/src/utils/colors.ts +++ b/packages/grafana-ui/src/utils/colors.ts @@ -10,7 +10,7 @@ export const NO_DATA_COLOR = 'rgba(150, 150, 150, 1)'; export const PENDING_COLOR = 'rgba(247, 149, 32, 1)'; export const REGION_FILL_ALPHA = 0.09; -const colors = [ +export const colors = [ '#7EB26D', // 0: pale green '#EAB839', // 1: mustard '#6ED0E0', // 2: light blue @@ -91,4 +91,3 @@ function hslToHex(color: any) { } export let sortedColors = sortColorsByHue(colors); -export default colors; diff --git a/packages/grafana-ui/src/utils/index.ts b/packages/grafana-ui/src/utils/index.ts index 1a677b1f033..eb67a6f7256 100644 --- a/packages/grafana-ui/src/utils/index.ts +++ b/packages/grafana-ui/src/utils/index.ts @@ -1,3 +1,2 @@ export * from './processTimeSeries'; export * from './colors'; -export { default } from './colors'; diff --git a/public/app/core/core.ts b/public/app/core/core.ts index 19e9a473e35..6713d8bcd14 100644 --- a/public/app/core/core.ts +++ b/public/app/core/core.ts @@ -16,6 +16,7 @@ import './utils/outline'; import './components/colorpicker/spectrum_picker'; import './services/search_srv'; import './services/ng_react'; +import { colors } from '@grafana/ui/'; import { searchDirective } from './components/search/search'; import { infoPopover } from './components/info_popover'; @@ -34,7 +35,6 @@ import 'app/core/services/all'; import './filters/filters'; import coreModule from './core_module'; import appEvents from './app_events'; -import colors from '@grafana/ui/'; import { assignModelProperties } from './utils/model_utils'; import { contextSrv } from './services/context_srv'; import { KeybindingSrv } from './services/keybindingSrv'; diff --git a/public/app/core/logs_model.ts b/public/app/core/logs_model.ts index 8eb8433cd53..4cf9a029a2a 100644 --- a/public/app/core/logs_model.ts +++ b/public/app/core/logs_model.ts @@ -1,5 +1,5 @@ import _ from 'lodash'; -import colors from '@grafana/ui'; +import { colors } from '@grafana/ui'; import { TimeSeries } from 'app/core/core'; import { getThemeColor } from 'app/core/utils/colors'; diff --git a/public/app/core/utils/explore.ts b/public/app/core/utils/explore.ts index 2cca33620b7..f3273ffa16d 100644 --- a/public/app/core/utils/explore.ts +++ b/public/app/core/utils/explore.ts @@ -1,5 +1,5 @@ import _ from 'lodash'; -import colors from '@grafana/ui'; +import { colors } from '@grafana/ui'; import { renderUrl } from 'app/core/utils/url'; import kbn from 'app/core/utils/kbn'; diff --git a/public/app/plugins/panel/graph/data_processor.ts b/public/app/plugins/panel/graph/data_processor.ts index ef4ad872753..4fe47b70129 100644 --- a/public/app/plugins/panel/graph/data_processor.ts +++ b/public/app/plugins/panel/graph/data_processor.ts @@ -1,5 +1,5 @@ import _ from 'lodash'; -import colors from '@grafana/ui'; +import { colors } from '@grafana/ui'; import TimeSeries from 'app/core/time_series2'; diff --git a/public/app/plugins/panel/graph2/GraphPanel.tsx b/public/app/plugins/panel/graph2/GraphPanel.tsx index 11be172aaeb..4a3daf77333 100644 --- a/public/app/plugins/panel/graph2/GraphPanel.tsx +++ b/public/app/plugins/panel/graph2/GraphPanel.tsx @@ -1,7 +1,7 @@ // Libraries import _ from 'lodash'; import React, { PureComponent } from 'react'; -import colors from '@grafana/ui'; +import { colors } from '@grafana/ui'; // Components & Types import { Graph, PanelProps, NullValueMode, processTimeSeries } from '@grafana/ui'; diff --git a/public/app/routes/GrafanaCtrl.ts b/public/app/routes/GrafanaCtrl.ts index 434a112692e..4e4dd8121cf 100644 --- a/public/app/routes/GrafanaCtrl.ts +++ b/public/app/routes/GrafanaCtrl.ts @@ -2,7 +2,7 @@ import config from 'app/core/config'; import _ from 'lodash'; import $ from 'jquery'; import Drop from 'tether-drop'; -import colors from '@grafana/ui'; +import { colors } from '@grafana/ui'; import coreModule from 'app/core/core_module'; import { profiler } from 'app/core/profiler'; diff --git a/public/app/viz/state/timeSeries.ts b/public/app/viz/state/timeSeries.ts index 2329d6f41af..5f27974a33b 100644 --- a/public/app/viz/state/timeSeries.ts +++ b/public/app/viz/state/timeSeries.ts @@ -2,7 +2,7 @@ import _ from 'lodash'; // Utils -import colors from '@grafana/ui'; +import { colors } from '@grafana/ui'; // Types import { TimeSeries, TimeSeriesVMs, NullValueMode } from '@grafana/ui'; From 1581662a6cd55bc9002c6ae0618ecb57e0056b04 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hugo=20H=C3=A4ggmark?= Date: Thu, 10 Jan 2019 09:37:32 +0100 Subject: [PATCH 72/91] Moved Thresholds and styles to grafana/ui/components --- .../src/components/Thresholds}/Threshold.test.tsx | 8 ++++---- .../grafana-ui/src/components/Thresholds}/Thresholds.tsx | 8 ++++---- .../grafana-ui/src/components/Thresholds/_Thresholds.scss | 0 packages/grafana-ui/src/components/index.scss | 1 + packages/grafana-ui/src/components/index.ts | 1 + packages/grafana-ui/src/types/panel.ts | 6 ++++++ public/app/plugins/panel/gauge/GaugePanelOptions.tsx | 3 +-- public/app/types/index.ts | 3 ++- public/app/types/panel.ts | 6 ------ public/sass/_grafana.scss | 1 - 10 files changed, 19 insertions(+), 18 deletions(-) rename {public/app/plugins/panel/gauge => packages/grafana-ui/src/components/Thresholds}/Threshold.test.tsx (91%) rename {public/app/plugins/panel/gauge => packages/grafana-ui/src/components/Thresholds}/Thresholds.tsx (96%) rename public/sass/components/_thresholds.scss => packages/grafana-ui/src/components/Thresholds/_Thresholds.scss (100%) diff --git a/public/app/plugins/panel/gauge/Threshold.test.tsx b/packages/grafana-ui/src/components/Thresholds/Threshold.test.tsx similarity index 91% rename from public/app/plugins/panel/gauge/Threshold.test.tsx rename to packages/grafana-ui/src/components/Thresholds/Threshold.test.tsx index 852b9f4c104..eac82e1b0f4 100644 --- a/public/app/plugins/panel/gauge/Threshold.test.tsx +++ b/packages/grafana-ui/src/components/Thresholds/Threshold.test.tsx @@ -1,10 +1,10 @@ import React from 'react'; import { shallow } from 'enzyme'; -import Thresholds from './Thresholds'; -import { defaultProps } from './GaugePanelOptions'; -import { BasicGaugeColor } from 'app/types'; import { PanelOptionsProps } from '@grafana/ui'; -import { Options } from './types'; +import { defaultProps } from 'app/plugins/panel/gauge/GaugePanelOptions'; +import { Options } from 'app/plugins/panel/gauge/types'; +import { BasicGaugeColor } from 'app/types'; +import { Thresholds } from './Thresholds'; const setup = (propOverrides?: object) => { const props: PanelOptionsProps = { diff --git a/public/app/plugins/panel/gauge/Thresholds.tsx b/packages/grafana-ui/src/components/Thresholds/Thresholds.tsx similarity index 96% rename from public/app/plugins/panel/gauge/Thresholds.tsx rename to packages/grafana-ui/src/components/Thresholds/Thresholds.tsx index 7699a499146..b5885e4efe8 100644 --- a/public/app/plugins/panel/gauge/Thresholds.tsx +++ b/packages/grafana-ui/src/components/Thresholds/Thresholds.tsx @@ -1,16 +1,16 @@ import React, { PureComponent } from 'react'; import tinycolor from 'tinycolor2'; import { ColorPicker } from '@grafana/ui'; -import { BasicGaugeColor, Threshold } from 'app/types'; -import { PanelOptionsProps } from '@grafana/ui'; -import { Options } from './types'; +import { BasicGaugeColor } from 'app/types'; +import { PanelOptionsProps, Threshold } from '@grafana/ui'; +import { Options } from 'app/plugins/panel/gauge/types'; interface State { thresholds: Threshold[]; baseColor: string; } -export default class Thresholds extends PureComponent, State> { +export class Thresholds extends PureComponent, State> { constructor(props) { super(props); diff --git a/public/sass/components/_thresholds.scss b/packages/grafana-ui/src/components/Thresholds/_Thresholds.scss similarity index 100% rename from public/sass/components/_thresholds.scss rename to packages/grafana-ui/src/components/Thresholds/_Thresholds.scss diff --git a/packages/grafana-ui/src/components/index.scss b/packages/grafana-ui/src/components/index.scss index e1d1474bb16..d0a81675490 100644 --- a/packages/grafana-ui/src/components/index.scss +++ b/packages/grafana-ui/src/components/index.scss @@ -1,3 +1,4 @@ @import 'CustomScrollbar/CustomScrollbar'; @import 'DeleteButton/DeleteButton'; +@import 'Thresholds/Thresholds'; @import 'Tooltip/Tooltip'; diff --git a/packages/grafana-ui/src/components/index.ts b/packages/grafana-ui/src/components/index.ts index b2b607415b8..fef3f6604c9 100644 --- a/packages/grafana-ui/src/components/index.ts +++ b/packages/grafana-ui/src/components/index.ts @@ -5,3 +5,4 @@ export { CustomScrollbar } from './CustomScrollbar/CustomScrollbar'; export { ColorPicker } from './ColorPicker/ColorPicker'; export { SeriesColorPickerPopover } from './ColorPicker/SeriesColorPickerPopover'; export { SeriesColorPicker } from './ColorPicker/SeriesColorPicker'; +export { Thresholds } from './Thresholds/Thresholds'; diff --git a/packages/grafana-ui/src/types/panel.ts b/packages/grafana-ui/src/types/panel.ts index 44336555a81..46fe84a211c 100644 --- a/packages/grafana-ui/src/types/panel.ts +++ b/packages/grafana-ui/src/types/panel.ts @@ -29,3 +29,9 @@ export interface PanelMenuItem { shortcut?: string; subMenu?: PanelMenuItem[]; } + +export interface Threshold { + index: number; + value: number; + color?: string; +} diff --git a/public/app/plugins/panel/gauge/GaugePanelOptions.tsx b/public/app/plugins/panel/gauge/GaugePanelOptions.tsx index 2b16ef5a1fe..7b627a09592 100644 --- a/public/app/plugins/panel/gauge/GaugePanelOptions.tsx +++ b/public/app/plugins/panel/gauge/GaugePanelOptions.tsx @@ -1,8 +1,7 @@ import React, { PureComponent } from 'react'; import ValueOptions from 'app/plugins/panel/gauge/ValueOptions'; -import Thresholds from 'app/plugins/panel/gauge/Thresholds'; import { BasicGaugeColor } from 'app/types'; -import { PanelOptionsProps } from '@grafana/ui'; +import { PanelOptionsProps, Thresholds } from '@grafana/ui'; import ValueMappings from 'app/plugins/panel/gauge/ValueMappings'; import { Options } from './types'; import GaugeOptions from './GaugeOptions'; diff --git a/public/app/types/index.ts b/public/app/types/index.ts index ab52b03ab17..52b2b996542 100644 --- a/public/app/types/index.ts +++ b/public/app/types/index.ts @@ -9,7 +9,7 @@ import { ApiKey, ApiKeysState, NewApiKey } from './apiKeys'; import { Invitee, OrgUser, User, UsersState, UserState } from './user'; import { DataSource, DataSourceSelectItem, DataSourcesState } from './datasources'; import { DataQuery, DataQueryResponse, DataQueryOptions } from './series'; -import { BasicGaugeColor, MappingType, RangeMap, Threshold, ValueMap } from './panel'; +import { BasicGaugeColor, MappingType, RangeMap, ValueMap } from './panel'; import { PluginDashboard, PluginMeta, Plugin, PanelPlugin, PluginsState } from './plugins'; import { Organization, OrganizationState } from './organization'; import { @@ -20,6 +20,7 @@ import { } from './appNotifications'; import { DashboardSearchHit } from './search'; import { ValidationEvents, ValidationRule } from './form'; +import { Threshold } from '@grafana/ui'; export { Team, TeamsState, diff --git a/public/app/types/panel.ts b/public/app/types/panel.ts index 31674d20304..1f5a2307733 100644 --- a/public/app/types/panel.ts +++ b/public/app/types/panel.ts @@ -1,9 +1,3 @@ -export interface Threshold { - index: number; - value: number; - color?: string; -} - export enum MappingType { ValueToText = 1, RangeToText = 2, diff --git a/public/sass/_grafana.scss b/public/sass/_grafana.scss index 10cc7335bdf..a3dd204eb63 100644 --- a/public/sass/_grafana.scss +++ b/public/sass/_grafana.scss @@ -102,7 +102,6 @@ @import 'components/toolbar'; @import 'components/add_data_source.scss'; @import 'components/page_loader'; -@import 'components/thresholds'; @import 'components/toggle_button_group'; @import 'components/value-mappings'; @import 'components/popover-box'; From 0b6e21e9acb529bd12ae0141977782d18e785d22 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hugo=20H=C3=A4ggmark?= Date: Thu, 10 Jan 2019 10:43:18 +0100 Subject: [PATCH 73/91] Renamed Thresholds to ThresholdsEditor --- .../grafana-ui/src/components/Thresholds/Threshold.test.tsx | 4 ++-- packages/grafana-ui/src/components/Thresholds/Thresholds.tsx | 2 +- packages/grafana-ui/src/components/index.ts | 2 +- public/app/plugins/panel/gauge/GaugePanelOptions.tsx | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/grafana-ui/src/components/Thresholds/Threshold.test.tsx b/packages/grafana-ui/src/components/Thresholds/Threshold.test.tsx index eac82e1b0f4..90ce6687985 100644 --- a/packages/grafana-ui/src/components/Thresholds/Threshold.test.tsx +++ b/packages/grafana-ui/src/components/Thresholds/Threshold.test.tsx @@ -4,7 +4,7 @@ import { PanelOptionsProps } from '@grafana/ui'; import { defaultProps } from 'app/plugins/panel/gauge/GaugePanelOptions'; import { Options } from 'app/plugins/panel/gauge/types'; import { BasicGaugeColor } from 'app/types'; -import { Thresholds } from './Thresholds'; +import { ThresholdsEditor } from './Thresholds'; const setup = (propOverrides?: object) => { const props: PanelOptionsProps = { @@ -17,7 +17,7 @@ const setup = (propOverrides?: object) => { Object.assign(props, propOverrides); - return shallow().instance() as Thresholds; + return shallow().instance() as ThresholdsEditor; }; describe('Add threshold', () => { diff --git a/packages/grafana-ui/src/components/Thresholds/Thresholds.tsx b/packages/grafana-ui/src/components/Thresholds/Thresholds.tsx index b5885e4efe8..d5af07e4a49 100644 --- a/packages/grafana-ui/src/components/Thresholds/Thresholds.tsx +++ b/packages/grafana-ui/src/components/Thresholds/Thresholds.tsx @@ -10,7 +10,7 @@ interface State { baseColor: string; } -export class Thresholds extends PureComponent, State> { +export class ThresholdsEditor extends PureComponent, State> { constructor(props) { super(props); diff --git a/packages/grafana-ui/src/components/index.ts b/packages/grafana-ui/src/components/index.ts index fef3f6604c9..79764ac8fa4 100644 --- a/packages/grafana-ui/src/components/index.ts +++ b/packages/grafana-ui/src/components/index.ts @@ -5,4 +5,4 @@ export { CustomScrollbar } from './CustomScrollbar/CustomScrollbar'; export { ColorPicker } from './ColorPicker/ColorPicker'; export { SeriesColorPickerPopover } from './ColorPicker/SeriesColorPickerPopover'; export { SeriesColorPicker } from './ColorPicker/SeriesColorPicker'; -export { Thresholds } from './Thresholds/Thresholds'; +export { ThresholdsEditor } from './Thresholds/Thresholds'; diff --git a/public/app/plugins/panel/gauge/GaugePanelOptions.tsx b/public/app/plugins/panel/gauge/GaugePanelOptions.tsx index 7b627a09592..2030b3c4cde 100644 --- a/public/app/plugins/panel/gauge/GaugePanelOptions.tsx +++ b/public/app/plugins/panel/gauge/GaugePanelOptions.tsx @@ -1,7 +1,7 @@ import React, { PureComponent } from 'react'; import ValueOptions from 'app/plugins/panel/gauge/ValueOptions'; import { BasicGaugeColor } from 'app/types'; -import { PanelOptionsProps, Thresholds } from '@grafana/ui'; +import { PanelOptionsProps, ThresholdsEditor } from '@grafana/ui'; import ValueMappings from 'app/plugins/panel/gauge/ValueMappings'; import { Options } from './types'; import GaugeOptions from './GaugeOptions'; @@ -33,7 +33,7 @@ export default class GaugePanelOptions extends PureComponent - +
From c05b92c2e0c9043a2e3933dd70cf700adf0e8970 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hugo=20H=C3=A4ggmark?= Date: Thu, 10 Jan 2019 10:47:49 +0100 Subject: [PATCH 74/91] Renamed Threshold files --- .../ThresholdsEditor.test.tsx} | 2 +- .../Thresholds.tsx => ThresholdsEditor/ThresholdsEditor.tsx} | 0 .../_ThresholdsEditor.scss} | 0 packages/grafana-ui/src/components/index.scss | 2 +- packages/grafana-ui/src/components/index.ts | 2 +- 5 files changed, 3 insertions(+), 3 deletions(-) rename packages/grafana-ui/src/components/{Thresholds/Threshold.test.tsx => ThresholdsEditor/ThresholdsEditor.test.tsx} (97%) rename packages/grafana-ui/src/components/{Thresholds/Thresholds.tsx => ThresholdsEditor/ThresholdsEditor.tsx} (100%) rename packages/grafana-ui/src/components/{Thresholds/_Thresholds.scss => ThresholdsEditor/_ThresholdsEditor.scss} (100%) diff --git a/packages/grafana-ui/src/components/Thresholds/Threshold.test.tsx b/packages/grafana-ui/src/components/ThresholdsEditor/ThresholdsEditor.test.tsx similarity index 97% rename from packages/grafana-ui/src/components/Thresholds/Threshold.test.tsx rename to packages/grafana-ui/src/components/ThresholdsEditor/ThresholdsEditor.test.tsx index 90ce6687985..6d6449aaa60 100644 --- a/packages/grafana-ui/src/components/Thresholds/Threshold.test.tsx +++ b/packages/grafana-ui/src/components/ThresholdsEditor/ThresholdsEditor.test.tsx @@ -4,7 +4,7 @@ import { PanelOptionsProps } from '@grafana/ui'; import { defaultProps } from 'app/plugins/panel/gauge/GaugePanelOptions'; import { Options } from 'app/plugins/panel/gauge/types'; import { BasicGaugeColor } from 'app/types'; -import { ThresholdsEditor } from './Thresholds'; +import { ThresholdsEditor } from './ThresholdsEditor'; const setup = (propOverrides?: object) => { const props: PanelOptionsProps = { diff --git a/packages/grafana-ui/src/components/Thresholds/Thresholds.tsx b/packages/grafana-ui/src/components/ThresholdsEditor/ThresholdsEditor.tsx similarity index 100% rename from packages/grafana-ui/src/components/Thresholds/Thresholds.tsx rename to packages/grafana-ui/src/components/ThresholdsEditor/ThresholdsEditor.tsx diff --git a/packages/grafana-ui/src/components/Thresholds/_Thresholds.scss b/packages/grafana-ui/src/components/ThresholdsEditor/_ThresholdsEditor.scss similarity index 100% rename from packages/grafana-ui/src/components/Thresholds/_Thresholds.scss rename to packages/grafana-ui/src/components/ThresholdsEditor/_ThresholdsEditor.scss diff --git a/packages/grafana-ui/src/components/index.scss b/packages/grafana-ui/src/components/index.scss index d0a81675490..cc5979c8444 100644 --- a/packages/grafana-ui/src/components/index.scss +++ b/packages/grafana-ui/src/components/index.scss @@ -1,4 +1,4 @@ @import 'CustomScrollbar/CustomScrollbar'; @import 'DeleteButton/DeleteButton'; -@import 'Thresholds/Thresholds'; +@import 'ThresholdsEditor/ThresholdsEditor'; @import 'Tooltip/Tooltip'; diff --git a/packages/grafana-ui/src/components/index.ts b/packages/grafana-ui/src/components/index.ts index 79764ac8fa4..4b60557be7e 100644 --- a/packages/grafana-ui/src/components/index.ts +++ b/packages/grafana-ui/src/components/index.ts @@ -5,4 +5,4 @@ export { CustomScrollbar } from './CustomScrollbar/CustomScrollbar'; export { ColorPicker } from './ColorPicker/ColorPicker'; export { SeriesColorPickerPopover } from './ColorPicker/SeriesColorPickerPopover'; export { SeriesColorPicker } from './ColorPicker/SeriesColorPicker'; -export { ThresholdsEditor } from './Thresholds/Thresholds'; +export { ThresholdsEditor } from './ThresholdsEditor/ThresholdsEditor'; From c54ec5f52f3bdac684a396ad9699306af29bcd94 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hugo=20H=C3=A4ggmark?= Date: Fri, 11 Jan 2019 08:30:30 +0100 Subject: [PATCH 75/91] Moved the rest of Threshold dependencies to ui/components --- .../ThresholdsEditor.test.tsx | 7 +-- .../ThresholdsEditor/ThresholdsEditor.tsx | 60 ++++++------------- .../grafana-ui/src/types/gauge.ts | 4 +- packages/grafana-ui/src/types/index.ts | 1 + packages/grafana-ui/src/types/panel.ts | 26 ++++++++ ...augeOptions.tsx => GaugeOptionsEditor.tsx} | 6 +- public/app/plugins/panel/gauge/GaugePanel.tsx | 6 +- .../plugins/panel/gauge/GaugePanelOptions.tsx | 11 ++-- public/app/plugins/panel/gauge/MappingRow.tsx | 3 +- .../panel/gauge/ValueMappings.test.tsx | 7 +-- .../app/plugins/panel/gauge/ValueMappings.tsx | 7 +-- .../app/plugins/panel/gauge/ValueOptions.tsx | 6 +- public/app/types/index.ts | 7 --- public/app/types/panel.ts | 25 -------- public/app/viz/Gauge.test.tsx | 4 +- public/app/viz/Gauge.tsx | 4 +- 16 files changed, 76 insertions(+), 108 deletions(-) rename public/app/plugins/panel/gauge/types.ts => packages/grafana-ui/src/types/gauge.ts (75%) rename public/app/plugins/panel/gauge/{GaugeOptions.tsx => GaugeOptionsEditor.tsx} (91%) delete mode 100644 public/app/types/panel.ts diff --git a/packages/grafana-ui/src/components/ThresholdsEditor/ThresholdsEditor.test.tsx b/packages/grafana-ui/src/components/ThresholdsEditor/ThresholdsEditor.test.tsx index 6d6449aaa60..8c0c131e1f8 100644 --- a/packages/grafana-ui/src/components/ThresholdsEditor/ThresholdsEditor.test.tsx +++ b/packages/grafana-ui/src/components/ThresholdsEditor/ThresholdsEditor.test.tsx @@ -1,13 +1,12 @@ import React from 'react'; import { shallow } from 'enzyme'; -import { PanelOptionsProps } from '@grafana/ui'; +import { BasicGaugeColor, GaugeOptions, PanelOptionsProps } from '@grafana/ui'; + import { defaultProps } from 'app/plugins/panel/gauge/GaugePanelOptions'; -import { Options } from 'app/plugins/panel/gauge/types'; -import { BasicGaugeColor } from 'app/types'; import { ThresholdsEditor } from './ThresholdsEditor'; const setup = (propOverrides?: object) => { - const props: PanelOptionsProps = { + const props: PanelOptionsProps = { onChange: jest.fn(), options: { ...defaultProps.options, diff --git a/packages/grafana-ui/src/components/ThresholdsEditor/ThresholdsEditor.tsx b/packages/grafana-ui/src/components/ThresholdsEditor/ThresholdsEditor.tsx index d5af07e4a49..df999de6c25 100644 --- a/packages/grafana-ui/src/components/ThresholdsEditor/ThresholdsEditor.tsx +++ b/packages/grafana-ui/src/components/ThresholdsEditor/ThresholdsEditor.tsx @@ -1,26 +1,20 @@ import React, { PureComponent } from 'react'; -import tinycolor from 'tinycolor2'; -import { ColorPicker } from '@grafana/ui'; -import { BasicGaugeColor } from 'app/types'; -import { PanelOptionsProps, Threshold } from '@grafana/ui'; -import { Options } from 'app/plugins/panel/gauge/types'; +import tinycolor, { ColorInput } from 'tinycolor2'; +import { BasicGaugeColor, ColorPicker, GaugeOptions, PanelOptionsProps, Threshold } from '@grafana/ui'; interface State { thresholds: Threshold[]; baseColor: string; } -export class ThresholdsEditor extends PureComponent, State> { - constructor(props) { +export class ThresholdsEditor extends PureComponent, State> { + constructor(props: PanelOptionsProps) { super(props); - this.state = { - thresholds: props.options.thresholds, - baseColor: props.options.baseColor, - }; + this.state = { thresholds: props.options.thresholds, baseColor: props.options.baseColor }; } - onAddThreshold = index => { + onAddThreshold = (index: number) => { const { maxValue, minValue } = this.props.options; const { thresholds } = this.state; @@ -48,27 +42,25 @@ export class ThresholdsEditor extends PureComponent, if (index === 0 && thresholds.length === 0) { color = tinycolor.mix(BasicGaugeColor.Green, BasicGaugeColor.Red, 50).toRgbString(); } else { - color = tinycolor.mix(thresholds[index - 1].color, BasicGaugeColor.Red, 50).toRgbString(); + color = tinycolor.mix(thresholds[index - 1].color as ColorInput, BasicGaugeColor.Red, 50).toRgbString(); } this.setState( { - thresholds: this.sortThresholds([...newThresholds, { index: index, value: value, color: color }]), + thresholds: this.sortThresholds([...newThresholds, { index, value: value as number, color }]), }, () => this.updateGauge() ); }; - onRemoveThreshold = threshold => { + onRemoveThreshold = (threshold: Threshold) => { this.setState( - prevState => ({ - thresholds: prevState.thresholds.filter(t => t !== threshold), - }), + prevState => ({ thresholds: prevState.thresholds.filter(t => t !== threshold) }), () => this.updateGauge() ); }; - onChangeThresholdValue = (event, threshold) => { + onChangeThresholdValue = (event: any, threshold: Threshold) => { const { thresholds } = this.state; const newThresholds = thresholds.map(t => { @@ -79,12 +71,10 @@ export class ThresholdsEditor extends PureComponent, return t; }); - this.setState({ - thresholds: newThresholds, - }); + this.setState({ thresholds: newThresholds }); }; - onChangeThresholdColor = (threshold, color) => { + onChangeThresholdColor = (threshold: Threshold, color: string) => { const { thresholds } = this.state; const newThresholds = thresholds.map(t => { @@ -103,11 +93,9 @@ export class ThresholdsEditor extends PureComponent, ); }; - onChangeBaseColor = color => this.props.onChange({ ...this.props.options, baseColor: color }); + onChangeBaseColor = (color: string) => this.props.onChange({ ...this.props.options, baseColor: color }); onBlur = () => { - this.setState(prevState => ({ - thresholds: this.sortThresholds(prevState.thresholds), - })); + this.setState(prevState => ({ thresholds: this.sortThresholds(prevState.thresholds) })); this.updateGauge(); }; @@ -116,7 +104,7 @@ export class ThresholdsEditor extends PureComponent, this.props.onChange({ ...this.props.options, thresholds: this.state.thresholds }); }; - sortThresholds = thresholds => { + sortThresholds = (thresholds: Threshold[]) => { return thresholds.sort((t1, t2) => { return t2.value - t1.value; }); @@ -161,20 +149,8 @@ export class ThresholdsEditor extends PureComponent, return thresholds.map((t, i) => { return (
-
this.onAddThreshold(t.index + 1)} - style={{ - height: '50%', - backgroundColor: t.color, - }} - /> -
this.onAddThreshold(t.index)} - style={{ - height: '50%', - backgroundColor: t.color, - }} - /> +
this.onAddThreshold(t.index + 1)} style={{ height: '50%', backgroundColor: t.color }} /> +
this.onAddThreshold(t.index)} style={{ height: '50%', backgroundColor: t.color }} />
); }); diff --git a/public/app/plugins/panel/gauge/types.ts b/packages/grafana-ui/src/types/gauge.ts similarity index 75% rename from public/app/plugins/panel/gauge/types.ts rename to packages/grafana-ui/src/types/gauge.ts index 60c4fd1581d..de9c7f07328 100644 --- a/public/app/plugins/panel/gauge/types.ts +++ b/packages/grafana-ui/src/types/gauge.ts @@ -1,6 +1,6 @@ -import { RangeMap, ValueMap, Threshold } from 'app/types'; +import { RangeMap, Threshold, ValueMap } from '@grafana/ui'; -export interface Options { +export interface GaugeOptions { baseColor: string; decimals: number; mappings: Array; diff --git a/packages/grafana-ui/src/types/index.ts b/packages/grafana-ui/src/types/index.ts index f618ce6db34..814ab0478db 100644 --- a/packages/grafana-ui/src/types/index.ts +++ b/packages/grafana-ui/src/types/index.ts @@ -1,3 +1,4 @@ export * from './series'; export * from './time'; export * from './panel'; +export * from './gauge'; diff --git a/packages/grafana-ui/src/types/panel.ts b/packages/grafana-ui/src/types/panel.ts index 46fe84a211c..0b995f932f0 100644 --- a/packages/grafana-ui/src/types/panel.ts +++ b/packages/grafana-ui/src/types/panel.ts @@ -35,3 +35,29 @@ export interface Threshold { value: number; color?: string; } + +export enum BasicGaugeColor { + Green = '#299c46', + Red = '#d44a3a', +} + +export enum MappingType { + ValueToText = 1, + RangeToText = 2, +} + +interface BaseMap { + id: number; + operator: string; + text: string; + type: MappingType; +} + +export interface ValueMap extends BaseMap { + value: string; +} + +export interface RangeMap extends BaseMap { + from: string; + to: string; +} diff --git a/public/app/plugins/panel/gauge/GaugeOptions.tsx b/public/app/plugins/panel/gauge/GaugeOptionsEditor.tsx similarity index 91% rename from public/app/plugins/panel/gauge/GaugeOptions.tsx rename to public/app/plugins/panel/gauge/GaugeOptionsEditor.tsx index 7607374b1b7..cb436180b49 100644 --- a/public/app/plugins/panel/gauge/GaugeOptions.tsx +++ b/public/app/plugins/panel/gauge/GaugeOptionsEditor.tsx @@ -1,10 +1,10 @@ import React, { PureComponent } from 'react'; +import { GaugeOptions, PanelOptionsProps } from '@grafana/ui'; + import { Switch } from 'app/core/components/Switch/Switch'; import { Label } from '../../../core/components/Label/Label'; -import { PanelOptionsProps } from '@grafana/ui'; -import { Options } from './types'; -export default class GaugeOptions extends PureComponent> { +export default class GaugeOptionsEditor extends PureComponent> { onToggleThresholdLabels = () => this.props.onChange({ ...this.props.options, showThresholdLabels: !this.props.options.showThresholdLabels }); diff --git a/public/app/plugins/panel/gauge/GaugePanel.tsx b/public/app/plugins/panel/gauge/GaugePanel.tsx index 5f1a438863f..79220daf37a 100644 --- a/public/app/plugins/panel/gauge/GaugePanel.tsx +++ b/public/app/plugins/panel/gauge/GaugePanel.tsx @@ -1,10 +1,10 @@ import React, { PureComponent } from 'react'; -import { PanelProps, NullValueMode } from '@grafana/ui'; +import { GaugeOptions, PanelProps, NullValueMode } from '@grafana/ui'; + import { getTimeSeriesVMs } from 'app/viz/state/timeSeries'; import Gauge from 'app/viz/Gauge'; -import { Options } from './types'; -interface Props extends PanelProps {} +interface Props extends PanelProps {} export class GaugePanel extends PureComponent { render() { diff --git a/public/app/plugins/panel/gauge/GaugePanelOptions.tsx b/public/app/plugins/panel/gauge/GaugePanelOptions.tsx index 2030b3c4cde..951a310d29a 100644 --- a/public/app/plugins/panel/gauge/GaugePanelOptions.tsx +++ b/public/app/plugins/panel/gauge/GaugePanelOptions.tsx @@ -1,10 +1,9 @@ import React, { PureComponent } from 'react'; +import { BasicGaugeColor, GaugeOptions, PanelOptionsProps, ThresholdsEditor } from '@grafana/ui'; + import ValueOptions from 'app/plugins/panel/gauge/ValueOptions'; -import { BasicGaugeColor } from 'app/types'; -import { PanelOptionsProps, ThresholdsEditor } from '@grafana/ui'; import ValueMappings from 'app/plugins/panel/gauge/ValueMappings'; -import { Options } from './types'; -import GaugeOptions from './GaugeOptions'; +import GaugeOptionsEditor from './GaugeOptionsEditor'; export const defaultProps = { options: { @@ -23,7 +22,7 @@ export const defaultProps = { }, }; -export default class GaugePanelOptions extends PureComponent> { +export default class GaugePanelOptions extends PureComponent> { static defaultProps = defaultProps; render() { @@ -32,7 +31,7 @@ export default class GaugePanelOptions extends PureComponent
- +
diff --git a/public/app/plugins/panel/gauge/MappingRow.tsx b/public/app/plugins/panel/gauge/MappingRow.tsx index 35d0b2e638c..5bf3b4ab907 100644 --- a/public/app/plugins/panel/gauge/MappingRow.tsx +++ b/public/app/plugins/panel/gauge/MappingRow.tsx @@ -1,7 +1,8 @@ import React, { PureComponent } from 'react'; +import { MappingType, RangeMap, ValueMap } from '@grafana/ui'; + import { Label } from 'app/core/components/Label/Label'; import { Select } from 'app/core/components/Select/Select'; -import { MappingType, RangeMap, ValueMap } from 'app/types'; interface Props { mapping: ValueMap | RangeMap; diff --git a/public/app/plugins/panel/gauge/ValueMappings.test.tsx b/public/app/plugins/panel/gauge/ValueMappings.test.tsx index 3e59cc76742..503e3e53617 100644 --- a/public/app/plugins/panel/gauge/ValueMappings.test.tsx +++ b/public/app/plugins/panel/gauge/ValueMappings.test.tsx @@ -1,13 +1,12 @@ import React from 'react'; import { shallow } from 'enzyme'; +import { GaugeOptions, MappingType, PanelOptionsProps } from '@grafana/ui'; + import ValueMappings from './ValueMappings'; -import { MappingType } from 'app/types'; -import { PanelOptionsProps } from '@grafana/ui'; -import { Options } from './types'; import { defaultProps } from 'app/plugins/panel/gauge/GaugePanelOptions'; const setup = (propOverrides?: object) => { - const props: PanelOptionsProps = { + const props: PanelOptionsProps = { onChange: jest.fn(), options: { ...defaultProps.options, diff --git a/public/app/plugins/panel/gauge/ValueMappings.tsx b/public/app/plugins/panel/gauge/ValueMappings.tsx index be800cf2412..4ce0d37b53c 100644 --- a/public/app/plugins/panel/gauge/ValueMappings.tsx +++ b/public/app/plugins/panel/gauge/ValueMappings.tsx @@ -1,15 +1,14 @@ import React, { PureComponent } from 'react'; +import { GaugeOptions, PanelOptionsProps, MappingType, RangeMap, ValueMap } from '@grafana/ui'; + import MappingRow from './MappingRow'; -import { MappingType, RangeMap, ValueMap } from 'app/types'; -import { PanelOptionsProps } from '@grafana/ui'; -import { Options } from './types'; interface State { mappings: Array; nextIdToAdd: number; } -export default class ValueMappings extends PureComponent, State> { +export default class ValueMappings extends PureComponent, State> { constructor(props) { super(props); diff --git a/public/app/plugins/panel/gauge/ValueOptions.tsx b/public/app/plugins/panel/gauge/ValueOptions.tsx index 4aafc0b0457..0d8771ec326 100644 --- a/public/app/plugins/panel/gauge/ValueOptions.tsx +++ b/public/app/plugins/panel/gauge/ValueOptions.tsx @@ -1,9 +1,9 @@ import React, { PureComponent } from 'react'; +import { GaugeOptions, PanelOptionsProps } from '@grafana/ui'; + import { Label } from 'app/core/components/Label/Label'; import Select from 'app/core/components/Select/Select'; import UnitPicker from 'app/core/components/Select/UnitPicker'; -import { PanelOptionsProps } from '@grafana/ui'; -import { Options } from './types'; const statOptions = [ { value: 'min', label: 'Min' }, @@ -21,7 +21,7 @@ const statOptions = [ const labelWidth = 6; -export default class ValueOptions extends PureComponent> { +export default class ValueOptions extends PureComponent> { onUnitChange = unit => this.props.onChange({ ...this.props.options, unit: unit.value }); onStatChange = stat => this.props.onChange({ ...this.props.options, stat: stat.value }); diff --git a/public/app/types/index.ts b/public/app/types/index.ts index 52b2b996542..72da1c76ea8 100644 --- a/public/app/types/index.ts +++ b/public/app/types/index.ts @@ -9,7 +9,6 @@ import { ApiKey, ApiKeysState, NewApiKey } from './apiKeys'; import { Invitee, OrgUser, User, UsersState, UserState } from './user'; import { DataSource, DataSourceSelectItem, DataSourcesState } from './datasources'; import { DataQuery, DataQueryResponse, DataQueryOptions } from './series'; -import { BasicGaugeColor, MappingType, RangeMap, ValueMap } from './panel'; import { PluginDashboard, PluginMeta, Plugin, PanelPlugin, PluginsState } from './plugins'; import { Organization, OrganizationState } from './organization'; import { @@ -20,7 +19,6 @@ import { } from './appNotifications'; import { DashboardSearchHit } from './search'; import { ValidationEvents, ValidationRule } from './form'; -import { Threshold } from '@grafana/ui'; export { Team, TeamsState, @@ -70,13 +68,8 @@ export { AppNotificationTimeout, DashboardSearchHit, UserState, - Threshold, ValidationEvents, ValidationRule, - ValueMap, - RangeMap, - MappingType, - BasicGaugeColor, }; export interface StoreState { diff --git a/public/app/types/panel.ts b/public/app/types/panel.ts deleted file mode 100644 index 1f5a2307733..00000000000 --- a/public/app/types/panel.ts +++ /dev/null @@ -1,25 +0,0 @@ -export enum MappingType { - ValueToText = 1, - RangeToText = 2, -} - -export enum BasicGaugeColor { - Green = '#299c46', - Red = '#d44a3a', -} - -interface BaseMap { - id: number; - operator: string; - text: string; - type: MappingType; -} - -export interface ValueMap extends BaseMap { - value: string; -} - -export interface RangeMap extends BaseMap { - from: string; - to: string; -} diff --git a/public/app/viz/Gauge.test.tsx b/public/app/viz/Gauge.test.tsx index 91107a563e5..f0c4a874649 100644 --- a/public/app/viz/Gauge.test.tsx +++ b/public/app/viz/Gauge.test.tsx @@ -1,8 +1,8 @@ import React from 'react'; import { shallow } from 'enzyme'; +import { BasicGaugeColor, TimeSeriesVMs } from '@grafana/ui'; + import { Gauge, Props } from './Gauge'; -import { BasicGaugeColor } from '../types'; -import { TimeSeriesVMs } from '@grafana/ui'; jest.mock('jquery', () => ({ plot: jest.fn(), diff --git a/public/app/viz/Gauge.tsx b/public/app/viz/Gauge.tsx index defeaf8cc8f..5112ff9aa1b 100644 --- a/public/app/viz/Gauge.tsx +++ b/public/app/viz/Gauge.tsx @@ -1,7 +1,7 @@ import React, { PureComponent } from 'react'; import $ from 'jquery'; -import { BasicGaugeColor, MappingType, RangeMap, Threshold, ValueMap } from 'app/types'; -import { TimeSeriesVMs } from '@grafana/ui'; +import { BasicGaugeColor, Threshold, TimeSeriesVMs, RangeMap, ValueMap, MappingType } from '@grafana/ui'; + import config from '../core/config'; import kbn from '../core/utils/kbn'; From 5ceedc4ac4aca6f33d86d93fd3ac84b6381350ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hugo=20H=C3=A4ggmark?= Date: Fri, 11 Jan 2019 09:16:53 +0100 Subject: [PATCH 76/91] Moved defaultProps to ui/components --- .../ThresholdsEditor.test.tsx | 2 +- .../ThresholdsEditor/ThresholdsEditor.tsx | 4 +++- packages/grafana-ui/src/types/gauge.ts | 19 ++++++++++++++++- .../plugins/panel/gauge/GaugePanelOptions.tsx | 21 ++----------------- .../panel/gauge/ValueMappings.test.tsx | 6 +++--- public/app/plugins/panel/gauge/module.tsx | 6 ++++-- 6 files changed, 31 insertions(+), 27 deletions(-) diff --git a/packages/grafana-ui/src/components/ThresholdsEditor/ThresholdsEditor.test.tsx b/packages/grafana-ui/src/components/ThresholdsEditor/ThresholdsEditor.test.tsx index 074d3bc267b..40e6bb47f1f 100644 --- a/packages/grafana-ui/src/components/ThresholdsEditor/ThresholdsEditor.test.tsx +++ b/packages/grafana-ui/src/components/ThresholdsEditor/ThresholdsEditor.test.tsx @@ -1,8 +1,8 @@ import React from 'react'; import { shallow } from 'enzyme'; -import { BasicGaugeColor, GaugeOptions, PanelOptionsProps } from '@grafana/ui'; import { ThresholdsEditor } from './ThresholdsEditor'; +import { BasicGaugeColor, PanelOptionsProps, GaugeOptions } from '../../types'; const defaultProps = { options: { diff --git a/packages/grafana-ui/src/components/ThresholdsEditor/ThresholdsEditor.tsx b/packages/grafana-ui/src/components/ThresholdsEditor/ThresholdsEditor.tsx index df999de6c25..ed6778f7c43 100644 --- a/packages/grafana-ui/src/components/ThresholdsEditor/ThresholdsEditor.tsx +++ b/packages/grafana-ui/src/components/ThresholdsEditor/ThresholdsEditor.tsx @@ -1,6 +1,8 @@ import React, { PureComponent } from 'react'; import tinycolor, { ColorInput } from 'tinycolor2'; -import { BasicGaugeColor, ColorPicker, GaugeOptions, PanelOptionsProps, Threshold } from '@grafana/ui'; + +import { Threshold, PanelOptionsProps, GaugeOptions, BasicGaugeColor } from '../../types'; +import { ColorPicker } from '../ColorPicker/ColorPicker'; interface State { thresholds: Threshold[]; diff --git a/packages/grafana-ui/src/types/gauge.ts b/packages/grafana-ui/src/types/gauge.ts index de9c7f07328..fe422386d92 100644 --- a/packages/grafana-ui/src/types/gauge.ts +++ b/packages/grafana-ui/src/types/gauge.ts @@ -1,4 +1,4 @@ -import { RangeMap, Threshold, ValueMap } from '@grafana/ui'; +import { BasicGaugeColor, RangeMap, Threshold, ValueMap } from './panel'; export interface GaugeOptions { baseColor: string; @@ -14,3 +14,20 @@ export interface GaugeOptions { thresholds: Threshold[]; unit: string; } + +export const GaugePanelOptionsDefaultProps = { + options: { + baseColor: BasicGaugeColor.Green, + minValue: 0, + maxValue: 100, + prefix: '', + showThresholdMarkers: true, + showThresholdLabels: false, + suffix: '', + decimals: 0, + stat: 'avg', + unit: 'none', + mappings: [], + thresholds: [], + }, +}; diff --git a/public/app/plugins/panel/gauge/GaugePanelOptions.tsx b/public/app/plugins/panel/gauge/GaugePanelOptions.tsx index 951a310d29a..99bff41a0d3 100644 --- a/public/app/plugins/panel/gauge/GaugePanelOptions.tsx +++ b/public/app/plugins/panel/gauge/GaugePanelOptions.tsx @@ -1,29 +1,12 @@ import React, { PureComponent } from 'react'; -import { BasicGaugeColor, GaugeOptions, PanelOptionsProps, ThresholdsEditor } from '@grafana/ui'; +import { GaugeOptions, GaugePanelOptionsDefaultProps, PanelOptionsProps, ThresholdsEditor } from '@grafana/ui'; import ValueOptions from 'app/plugins/panel/gauge/ValueOptions'; import ValueMappings from 'app/plugins/panel/gauge/ValueMappings'; import GaugeOptionsEditor from './GaugeOptionsEditor'; -export const defaultProps = { - options: { - baseColor: BasicGaugeColor.Green, - minValue: 0, - maxValue: 100, - prefix: '', - showThresholdMarkers: true, - showThresholdLabels: false, - suffix: '', - decimals: 0, - stat: 'avg', - unit: 'none', - mappings: [], - thresholds: [], - }, -}; - export default class GaugePanelOptions extends PureComponent> { - static defaultProps = defaultProps; + static defaultProps = GaugePanelOptionsDefaultProps; render() { const { onChange, options } = this.props; diff --git a/public/app/plugins/panel/gauge/ValueMappings.test.tsx b/public/app/plugins/panel/gauge/ValueMappings.test.tsx index 503e3e53617..0cf08d6d3b7 100644 --- a/public/app/plugins/panel/gauge/ValueMappings.test.tsx +++ b/public/app/plugins/panel/gauge/ValueMappings.test.tsx @@ -1,15 +1,15 @@ import React from 'react'; import { shallow } from 'enzyme'; import { GaugeOptions, MappingType, PanelOptionsProps } from '@grafana/ui'; +import { GaugePanelOptionsDefaultProps } from '@grafana/ui/src/types/gauge'; import ValueMappings from './ValueMappings'; -import { defaultProps } from 'app/plugins/panel/gauge/GaugePanelOptions'; const setup = (propOverrides?: object) => { const props: PanelOptionsProps = { onChange: jest.fn(), options: { - ...defaultProps.options, + ...GaugePanelOptionsDefaultProps.options, mappings: [ { id: 1, operator: '', type: MappingType.ValueToText, value: '20', text: 'Ok' }, { id: 2, operator: '', type: MappingType.RangeToText, from: '21', to: '30', text: 'Meh' }, @@ -67,7 +67,7 @@ describe('Next id to add', () => { }); it('should default to 1', () => { - const { instance } = setup({ options: { ...defaultProps.options } }); + const { instance } = setup({ options: { ...GaugePanelOptionsDefaultProps.options } }); expect(instance.state.nextIdToAdd).toEqual(1); }); diff --git a/public/app/plugins/panel/gauge/module.tsx b/public/app/plugins/panel/gauge/module.tsx index 783e4825657..72230eb4ba3 100644 --- a/public/app/plugins/panel/gauge/module.tsx +++ b/public/app/plugins/panel/gauge/module.tsx @@ -1,4 +1,6 @@ -import GaugePanelOptions, { defaultProps } from './GaugePanelOptions'; +import { GaugePanelOptionsDefaultProps } from '@grafana/ui'; + +import GaugePanelOptions from './GaugePanelOptions'; import { GaugePanel } from './GaugePanel'; -export { GaugePanel as Panel, GaugePanelOptions as PanelOptions, defaultProps as PanelDefaults }; +export { GaugePanel as Panel, GaugePanelOptions as PanelOptions, GaugePanelOptionsDefaultProps as PanelDefaults }; From 537e2534a64b08fdb7e633c2bc7cd5161c969986 Mon Sep 17 00:00:00 2001 From: Dominik Prokop Date: Fri, 11 Jan 2019 09:50:05 +0100 Subject: [PATCH 77/91] Move Select styles to grafana/ui --- .../grafana-ui/src/components/Select/_Select.scss | 0 packages/grafana-ui/src/components/index.scss | 1 + public/sass/_grafana.scss | 9 ++++----- 3 files changed, 5 insertions(+), 5 deletions(-) rename public/sass/components/_form_select_box.scss => packages/grafana-ui/src/components/Select/_Select.scss (100%) diff --git a/public/sass/components/_form_select_box.scss b/packages/grafana-ui/src/components/Select/_Select.scss similarity index 100% rename from public/sass/components/_form_select_box.scss rename to packages/grafana-ui/src/components/Select/_Select.scss diff --git a/packages/grafana-ui/src/components/index.scss b/packages/grafana-ui/src/components/index.scss index e1d1474bb16..77a2caa9c5c 100644 --- a/packages/grafana-ui/src/components/index.scss +++ b/packages/grafana-ui/src/components/index.scss @@ -1,3 +1,4 @@ @import 'CustomScrollbar/CustomScrollbar'; @import 'DeleteButton/DeleteButton'; @import 'Tooltip/Tooltip'; +@import 'Select/Select'; diff --git a/public/sass/_grafana.scss b/public/sass/_grafana.scss index 10cc7335bdf..c8ad1ce8edc 100644 --- a/public/sass/_grafana.scss +++ b/public/sass/_grafana.scss @@ -1,4 +1,4 @@ -// DEPENDENCIES + // DEPENDENCIES @import '../../node_modules/react-table/react-table.css'; // VENDOR @@ -38,9 +38,6 @@ @import 'layout/lists'; @import 'layout/page'; -// LOAD @grafana/ui components -@import '../../packages/grafana-ui/src/index'; - // COMPONENTS @import 'components/scrollbar'; @import 'components/cards'; @@ -97,7 +94,6 @@ @import 'components/page_header'; @import 'components/dashboard_settings'; @import 'components/empty_list_cta'; -@import 'components/form_select_box'; @import 'components/panel_editor'; @import 'components/toolbar'; @import 'components/add_data_source.scss'; @@ -107,6 +103,9 @@ @import 'components/value-mappings'; @import 'components/popover-box'; +// LOAD @grafana/ui components +@import '../../packages/grafana-ui/src/index'; + // PAGES @import 'pages/login'; @import 'pages/dashboard'; From d2b71cff3716f19456ea0e0d261f6924f5f9f245 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hugo=20H=C3=A4ggmark?= Date: Fri, 11 Jan 2019 11:25:49 +0100 Subject: [PATCH 78/91] Reverted move of defaults for GaugePanelOptions --- .../ThresholdsEditor.test.tsx | 33 +++------------- .../ThresholdsEditor/ThresholdsEditor.tsx | 38 +++++++++++++------ packages/grafana-ui/src/types/gauge.ts | 19 +--------- .../plugins/panel/gauge/GaugePanelOptions.tsx | 25 ++++++++++-- .../panel/gauge/ValueMappings.test.tsx | 6 +-- public/app/plugins/panel/gauge/module.tsx | 6 +-- 6 files changed, 60 insertions(+), 67 deletions(-) diff --git a/packages/grafana-ui/src/components/ThresholdsEditor/ThresholdsEditor.test.tsx b/packages/grafana-ui/src/components/ThresholdsEditor/ThresholdsEditor.test.tsx index 40e6bb47f1f..14f84e00f80 100644 --- a/packages/grafana-ui/src/components/ThresholdsEditor/ThresholdsEditor.test.tsx +++ b/packages/grafana-ui/src/components/ThresholdsEditor/ThresholdsEditor.test.tsx @@ -1,33 +1,13 @@ import React from 'react'; import { shallow } from 'enzyme'; -import { ThresholdsEditor } from './ThresholdsEditor'; -import { BasicGaugeColor, PanelOptionsProps, GaugeOptions } from '../../types'; - -const defaultProps = { - options: { - baseColor: BasicGaugeColor.Green, - minValue: 0, - maxValue: 100, - prefix: '', - showThresholdMarkers: true, - showThresholdLabels: false, - suffix: '', - decimals: 0, - stat: 'avg', - unit: 'none', - mappings: [], - thresholds: [], - }, -}; +import { ThresholdsEditor, Props } from './ThresholdsEditor'; +import { BasicGaugeColor } from '../../types'; const setup = (propOverrides?: object) => { - const props: PanelOptionsProps = { + const props: Props = { onChange: jest.fn(), - options: { - ...defaultProps.options, - thresholds: [], - }, + thresholds: [], }; Object.assign(props, propOverrides); @@ -46,10 +26,7 @@ describe('Add threshold', () => { it('should add another threshold above a first', () => { const instance = setup({ - options: { - ...defaultProps.options, - thresholds: [{ index: 0, value: 50, color: 'rgb(127, 115, 64)' }], - }, + thresholds: [{ index: 0, value: 50, color: 'rgb(127, 115, 64)' }], }); instance.onAddThreshold(1); diff --git a/packages/grafana-ui/src/components/ThresholdsEditor/ThresholdsEditor.tsx b/packages/grafana-ui/src/components/ThresholdsEditor/ThresholdsEditor.tsx index ed6778f7c43..54165dfadb5 100644 --- a/packages/grafana-ui/src/components/ThresholdsEditor/ThresholdsEditor.tsx +++ b/packages/grafana-ui/src/components/ThresholdsEditor/ThresholdsEditor.tsx @@ -1,28 +1,37 @@ import React, { PureComponent } from 'react'; import tinycolor, { ColorInput } from 'tinycolor2'; -import { Threshold, PanelOptionsProps, GaugeOptions, BasicGaugeColor } from '../../types'; +import { Threshold, BasicGaugeColor } from '../../types'; import { ColorPicker } from '../ColorPicker/ColorPicker'; +export interface Props { + thresholds: Threshold[]; + onChange: (thresholds: Threshold[]) => void; +} + interface State { thresholds: Threshold[]; baseColor: string; } -export class ThresholdsEditor extends PureComponent, State> { - constructor(props: PanelOptionsProps) { +export class ThresholdsEditor extends PureComponent { + constructor(props: Props) { super(props); - this.state = { thresholds: props.options.thresholds, baseColor: props.options.baseColor }; + this.state = { thresholds: props.thresholds, baseColor: BasicGaugeColor.Green }; } onAddThreshold = (index: number) => { - const { maxValue, minValue } = this.props.options; + const maxValue = 100; // hardcoded for now before we add the base threshold + const minValue = 0; // hardcoded for now before we add the base threshold const { thresholds } = this.state; const newThresholds = thresholds.map(threshold => { if (threshold.index >= index) { - threshold = { ...threshold, index: threshold.index + 1 }; + threshold = { + ...threshold, + index: threshold.index + 1, + }; } return threshold; @@ -49,7 +58,14 @@ export class ThresholdsEditor extends PureComponent this.updateGauge() ); @@ -95,7 +111,7 @@ export class ThresholdsEditor extends PureComponent this.props.onChange({ ...this.props.options, baseColor: color }); + onChangeBaseColor = (color: string) => this.props.onChange(this.state.thresholds); onBlur = () => { this.setState(prevState => ({ thresholds: this.sortThresholds(prevState.thresholds) })); @@ -103,7 +119,7 @@ export class ThresholdsEditor extends PureComponent { - this.props.onChange({ ...this.props.options, thresholds: this.state.thresholds }); + this.props.onChange(this.state.thresholds); }; sortThresholds = (thresholds: Threshold[]) => { @@ -163,14 +179,14 @@ export class ThresholdsEditor extends PureComponent
this.onAddThreshold(0)} - style={{ height: '100%', backgroundColor: this.props.options.baseColor }} + style={{ height: '100%', backgroundColor: BasicGaugeColor.Green }} />
); } renderBase() { - const { baseColor } = this.props.options; + const baseColor = BasicGaugeColor.Green; return (
diff --git a/packages/grafana-ui/src/types/gauge.ts b/packages/grafana-ui/src/types/gauge.ts index fe422386d92..e05849448f7 100644 --- a/packages/grafana-ui/src/types/gauge.ts +++ b/packages/grafana-ui/src/types/gauge.ts @@ -1,4 +1,4 @@ -import { BasicGaugeColor, RangeMap, Threshold, ValueMap } from './panel'; +import { RangeMap, Threshold, ValueMap } from './panel'; export interface GaugeOptions { baseColor: string; @@ -14,20 +14,3 @@ export interface GaugeOptions { thresholds: Threshold[]; unit: string; } - -export const GaugePanelOptionsDefaultProps = { - options: { - baseColor: BasicGaugeColor.Green, - minValue: 0, - maxValue: 100, - prefix: '', - showThresholdMarkers: true, - showThresholdLabels: false, - suffix: '', - decimals: 0, - stat: 'avg', - unit: 'none', - mappings: [], - thresholds: [], - }, -}; diff --git a/public/app/plugins/panel/gauge/GaugePanelOptions.tsx b/public/app/plugins/panel/gauge/GaugePanelOptions.tsx index 99bff41a0d3..e43abad61a3 100644 --- a/public/app/plugins/panel/gauge/GaugePanelOptions.tsx +++ b/public/app/plugins/panel/gauge/GaugePanelOptions.tsx @@ -1,12 +1,31 @@ import React, { PureComponent } from 'react'; -import { GaugeOptions, GaugePanelOptionsDefaultProps, PanelOptionsProps, ThresholdsEditor } from '@grafana/ui'; +import { BasicGaugeColor, GaugeOptions, PanelOptionsProps, ThresholdsEditor, Threshold } from '@grafana/ui'; import ValueOptions from 'app/plugins/panel/gauge/ValueOptions'; import ValueMappings from 'app/plugins/panel/gauge/ValueMappings'; import GaugeOptionsEditor from './GaugeOptionsEditor'; +export const defaultProps = { + options: { + baseColor: BasicGaugeColor.Green, + minValue: 0, + maxValue: 100, + prefix: '', + showThresholdMarkers: true, + showThresholdLabels: false, + suffix: '', + decimals: 0, + stat: 'avg', + unit: 'none', + mappings: [], + thresholds: [], + }, +}; + export default class GaugePanelOptions extends PureComponent> { - static defaultProps = GaugePanelOptionsDefaultProps; + static defaultProps = defaultProps; + + onThresholdsChanged = (thresholds: Threshold[]) => this.props.onChange({ ...this.props.options, thresholds }); render() { const { onChange, options } = this.props; @@ -15,7 +34,7 @@ export default class GaugePanelOptions extends PureComponent - +
diff --git a/public/app/plugins/panel/gauge/ValueMappings.test.tsx b/public/app/plugins/panel/gauge/ValueMappings.test.tsx index 0cf08d6d3b7..07db4028c68 100644 --- a/public/app/plugins/panel/gauge/ValueMappings.test.tsx +++ b/public/app/plugins/panel/gauge/ValueMappings.test.tsx @@ -1,7 +1,7 @@ import React from 'react'; import { shallow } from 'enzyme'; import { GaugeOptions, MappingType, PanelOptionsProps } from '@grafana/ui'; -import { GaugePanelOptionsDefaultProps } from '@grafana/ui/src/types/gauge'; +import { defaultProps } from 'app/plugins/panel/gauge/GaugePanelOptions'; import ValueMappings from './ValueMappings'; @@ -9,7 +9,7 @@ const setup = (propOverrides?: object) => { const props: PanelOptionsProps = { onChange: jest.fn(), options: { - ...GaugePanelOptionsDefaultProps.options, + ...defaultProps.options, mappings: [ { id: 1, operator: '', type: MappingType.ValueToText, value: '20', text: 'Ok' }, { id: 2, operator: '', type: MappingType.RangeToText, from: '21', to: '30', text: 'Meh' }, @@ -67,7 +67,7 @@ describe('Next id to add', () => { }); it('should default to 1', () => { - const { instance } = setup({ options: { ...GaugePanelOptionsDefaultProps.options } }); + const { instance } = setup({ options: { ...defaultProps.options } }); expect(instance.state.nextIdToAdd).toEqual(1); }); diff --git a/public/app/plugins/panel/gauge/module.tsx b/public/app/plugins/panel/gauge/module.tsx index 72230eb4ba3..783e4825657 100644 --- a/public/app/plugins/panel/gauge/module.tsx +++ b/public/app/plugins/panel/gauge/module.tsx @@ -1,6 +1,4 @@ -import { GaugePanelOptionsDefaultProps } from '@grafana/ui'; - -import GaugePanelOptions from './GaugePanelOptions'; +import GaugePanelOptions, { defaultProps } from './GaugePanelOptions'; import { GaugePanel } from './GaugePanel'; -export { GaugePanel as Panel, GaugePanelOptions as PanelOptions, GaugePanelOptionsDefaultProps as PanelDefaults }; +export { GaugePanel as Panel, GaugePanelOptions as PanelOptions, defaultProps as PanelDefaults }; 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 79/91] 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 80/91] 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 81/91] 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 82/91] 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 83/91] 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 84/91] 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 85/91] 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 86/91] 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 87/91] 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 88/91] 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 89/91] 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 b1f5a232da7b9acb6026fec87f5bba6cbd2180c3 Mon Sep 17 00:00:00 2001 From: Leonard Gram Date: Mon, 14 Jan 2019 13:42:58 +0100 Subject: [PATCH 90/91] 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 91/91] 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); }