diff --git a/packages/grafana-ui/src/components/StatsPicker/StatsPicker.tsx b/packages/grafana-ui/src/components/StatsPicker/StatsPicker.tsx index 22ba5423b30..d15c7ebfcd8 100644 --- a/packages/grafana-ui/src/components/StatsPicker/StatsPicker.tsx +++ b/packages/grafana-ui/src/components/StatsPicker/StatsPicker.tsx @@ -69,7 +69,7 @@ export class StatsPicker extends PureComponent { return { value: s.id, label: s.name, - desctipiton: s.description, + description: s.description, }; }); diff --git a/packages/grafana-ui/src/components/index.ts b/packages/grafana-ui/src/components/index.ts index 7f379669696..4e81dc732e2 100644 --- a/packages/grafana-ui/src/components/index.ts +++ b/packages/grafana-ui/src/components/index.ts @@ -27,6 +27,7 @@ export { Switch } from './Switch/Switch'; export { EmptySearchResult } from './EmptySearchResult/EmptySearchResult'; export { PieChart, PieChartDataPoint, PieChartType } from './PieChart/PieChart'; export { UnitPicker } from './UnitPicker/UnitPicker'; +export { StatsPicker } from './StatsPicker/StatsPicker'; export { Input, InputStatus } from './Input/Input'; // Visualizations diff --git a/packages/grafana-ui/src/utils/index.ts b/packages/grafana-ui/src/utils/index.ts index 7e7c2e30ebf..eb05e828616 100644 --- a/packages/grafana-ui/src/utils/index.ts +++ b/packages/grafana-ui/src/utils/index.ts @@ -5,6 +5,7 @@ export * from './colors'; export * from './namedColorsPalette'; export * from './thresholds'; export * from './string'; +export * from './statsCalculator'; export * from './displayValue'; export * from './deprecationWarning'; export { getMappedValue } from './valueMappings'; diff --git a/packages/grafana-ui/src/utils/statsCalculator.test.ts b/packages/grafana-ui/src/utils/statsCalculator.test.ts index 2523e2727ef..606f510f26f 100644 --- a/packages/grafana-ui/src/utils/statsCalculator.test.ts +++ b/packages/grafana-ui/src/utils/statsCalculator.test.ts @@ -41,7 +41,7 @@ describe('Stats Calculators', () => { it('should calculate basic stats', () => { const stats = calculateStats({ - data: basicTable, + table: basicTable, columnIndex: 0, stats: ['first', 'last', 'mean'], }); @@ -58,7 +58,7 @@ describe('Stats Calculators', () => { it('should support a single stat also', () => { const stats = calculateStats({ - data: basicTable, + table: basicTable, columnIndex: 0, stats: ['first'], }); @@ -70,7 +70,7 @@ describe('Stats Calculators', () => { it('should get non standard stats', () => { const stats = calculateStats({ - data: basicTable, + table: basicTable, columnIndex: 0, stats: [StatID.distinctCount, StatID.changeCount], }); @@ -78,4 +78,15 @@ describe('Stats Calculators', () => { expect(stats.distinctCount).toEqual(2); expect(stats.changeCount).toEqual(1); }); + + it('should calculate step', () => { + const stats = calculateStats({ + table: { columns: [{ text: 'A' }], rows: [[100], [200], [300], [400]] }, + columnIndex: 0, + stats: [StatID.step, StatID.delta], + }); + + expect(stats.step).toEqual(100); + expect(stats.delta).toEqual(300); + }); }); diff --git a/packages/grafana-ui/src/utils/statsCalculator.ts b/packages/grafana-ui/src/utils/statsCalculator.ts index 4cf679136f2..5fb03408c17 100644 --- a/packages/grafana-ui/src/utils/statsCalculator.ts +++ b/packages/grafana-ui/src/utils/statsCalculator.ts @@ -29,7 +29,7 @@ export interface ColumnStats { } // Internal function -type StatCalculator = (data: TableData, columnIndex: number, ignoreNulls: boolean, nullAsZero: boolean) => ColumnStats; +type StatCalculator = (table: TableData, columnIndex: number, ignoreNulls: boolean, nullAsZero: boolean) => ColumnStats; export interface StatCalculatorInfo { id: string; @@ -64,7 +64,7 @@ export function getStatsCalculators(ids?: string[]): StatCalculatorInfo[] { } export interface CalculateStatsOptions { - data: TableData; + table: TableData; columnIndex: number; stats: string[]; // The stats to calculate nullValueMode?: NullValueMode; @@ -74,7 +74,7 @@ export interface CalculateStatsOptions { * @returns an object with a key for each selected stat */ export function calculateStats(options: CalculateStatsOptions): ColumnStats { - const { data, columnIndex, stats, nullValueMode } = options; + const { table, columnIndex, stats, nullValueMode } = options; if (!stats || stats.length < 1) { return {}; @@ -84,7 +84,7 @@ export function calculateStats(options: CalculateStatsOptions): ColumnStats { // Return early for empty tables // This lets the concrete implementations assume at least one row - if (!data.rows || data.rows.length < 1) { + if (!table.rows || table.rows.length < 1) { const stats = {} as ColumnStats; queue.forEach(stat => { stats[stat.id] = stat.emptyInputResult !== null ? stat.emptyInputResult : null; @@ -97,19 +97,19 @@ export function calculateStats(options: CalculateStatsOptions): ColumnStats { // Avoid calculating all the standard stats if possible if (queue.length === 1 && queue[0].calculator) { - return queue[0].calculator(data, columnIndex, ignoreNulls, nullAsZero); + return queue[0].calculator(table, columnIndex, ignoreNulls, nullAsZero); } // For now everything can use the standard stats - let values = standardStatsStat(data, columnIndex, ignoreNulls, nullAsZero); - queue.forEach(calc => { + let values = standardStatsStat(table, columnIndex, ignoreNulls, nullAsZero); + for (const calc of queue) { if (!values.hasOwnProperty(calc.id) && calc.calculator) { values = { ...values, - ...calc.calculator(data, columnIndex, ignoreNulls, nullAsZero), + ...calc.calculator(table, columnIndex, ignoreNulls, nullAsZero), }; } - }); + } return values; } @@ -141,7 +141,7 @@ function getById(id: string): StatCalculatorInfo | undefined { { id: StatID.first, name: 'First', description: 'First Value', standard: true, calculator: calculateFirst }, { id: StatID.min, name: 'Min', description: 'Minimum Value', standard: true }, { id: StatID.max, name: 'Max', description: 'Maximum Value', standard: true }, - { id: StatID.mean, name: 'Mean', description: 'Average Value', standard: true }, + { id: StatID.mean, name: 'Mean', description: 'Average Value', standard: true, alias: 'avg' }, { id: StatID.sum, name: 'Total', @@ -241,7 +241,7 @@ function standardStatsStat( range: null, diff: null, delta: 0, - step: 0, + step: Number.MAX_VALUE, // Just used for calcutations -- not exposed as a stat previousDeltaUp: true, @@ -260,8 +260,6 @@ function standardStatsStat( } if (currentValue !== null) { - stats.last = currentValue; - const isFirst = stats.first === null; if (isFirst) { stats.first = currentValue; @@ -324,6 +322,10 @@ function standardStatsStat( stats.min = null; } + if (stats.step === Number.MAX_VALUE) { + stats.step = null; + } + if (stats.nonNullCount > 0) { stats.mean = stats.sum! / stats.nonNullCount; } @@ -342,7 +344,6 @@ function standardStatsStat( } function calculateFirst(data: TableData, columnIndex: number, ignoreNulls: boolean, nullAsZero: boolean): ColumnStats { - console.log('FIRST', data); return { first: data.rows[0][columnIndex] }; } diff --git a/public/app/features/dashboard/dashgrid/DashboardPanel.tsx b/public/app/features/dashboard/dashgrid/DashboardPanel.tsx index 029fb0e8371..d7745c8b7fd 100644 --- a/public/app/features/dashboard/dashgrid/DashboardPanel.tsx +++ b/public/app/features/dashboard/dashgrid/DashboardPanel.tsx @@ -98,6 +98,12 @@ export class DashboardPanel extends PureComponent { } panel.changeType(pluginId, hook); } + } else if (plugin.exports && plugin.exports.reactPanel) { + const hook = plugin.exports.reactPanel.panelTypeChangedHook; + if (hook) { + panel.options = hook(panel.options || {}, null, null); + console.log('OPITONS', pluginId, panel); + } } this.setState({ plugin, angularPanel: null }); diff --git a/public/app/plugins/panel/bargauge/types.ts b/public/app/plugins/panel/bargauge/types.ts index 4a22a64bdd4..2cfd6bdd653 100644 --- a/public/app/plugins/panel/bargauge/types.ts +++ b/public/app/plugins/panel/bargauge/types.ts @@ -1,4 +1,4 @@ -import { VizOrientation, SelectOptionItem } from '@grafana/ui'; +import { VizOrientation, SelectOptionItem, StatID } from '@grafana/ui'; import { SingleStatBaseOptions } from '../singlestat2/types'; export interface BarGaugeOptions extends SingleStatBaseOptions { @@ -25,7 +25,7 @@ export const defaults: BarGaugeOptions = { orientation: VizOrientation.Horizontal, valueOptions: { unit: 'none', - stat: 'avg', + stat: StatID.mean, prefix: '', suffix: '', decimals: null, diff --git a/public/app/plugins/panel/gauge/types.ts b/public/app/plugins/panel/gauge/types.ts index bab29d5d2ad..a409e3524c0 100644 --- a/public/app/plugins/panel/gauge/types.ts +++ b/public/app/plugins/panel/gauge/types.ts @@ -1,5 +1,5 @@ import { SingleStatBaseOptions } from '../singlestat2/types'; -import { VizOrientation } from '@grafana/ui'; +import { VizOrientation, StatID } from '@grafana/ui'; export interface GaugeOptions extends SingleStatBaseOptions { maxValue: number; @@ -17,7 +17,7 @@ export const defaults: GaugeOptions = { prefix: '', suffix: '', decimals: null, - stat: 'avg', + stat: StatID.mean, unit: 'none', }, valueMappings: [], diff --git a/public/app/plugins/panel/singlestat2/SingleStatPanel.tsx b/public/app/plugins/panel/singlestat2/SingleStatPanel.tsx index 1193a031c94..fc067859f0d 100644 --- a/public/app/plugins/panel/singlestat2/SingleStatPanel.tsx +++ b/public/app/plugins/panel/singlestat2/SingleStatPanel.tsx @@ -4,7 +4,7 @@ import React, { PureComponent, CSSProperties } from 'react'; // Types import { SingleStatOptions, SingleStatBaseOptions } from './types'; -import { DisplayValue, PanelProps, processTimeSeries, NullValueMode } from '@grafana/ui'; +import { DisplayValue, PanelProps, NullValueMode, calculateStats } from '@grafana/ui'; import { config } from 'app/core/config'; import { getDisplayProcessor } from '@grafana/ui'; import { ProcessedValuesRepeater } from './ProcessedValuesRepeater'; @@ -14,7 +14,7 @@ export const getSingleStatValues = (props: PanelProps): D const { valueOptions, valueMappings } = options; const { unit, decimals, stat } = valueOptions; - const processor = getDisplayProcessor({ + const display = getDisplayProcessor({ unit, decimals, mappings: valueMappings, @@ -24,12 +24,20 @@ export const getSingleStatValues = (props: PanelProps): D theme: config.theme, }); - return processTimeSeries({ - data, - nullValueMode: NullValueMode.Null, - }).map((series, index) => { - const value = stat !== 'name' ? series.stats[stat] : series.label; - return processor(value); + return data.map(table => { + // Support last_time? Add that to the migration? last, but differnt column + if (stat === 'name') { + // Should not really be possible anymore? + return display(table.name); + } + return display( + calculateStats({ + table, + columnIndex: 0, // Hardcoded for now! + stats: [stat], // The stats to calculate + nullValueMode: NullValueMode.Null, + })[stat] + ); }); }; diff --git a/public/app/plugins/panel/singlestat2/SingleStatValueEditor.tsx b/public/app/plugins/panel/singlestat2/SingleStatValueEditor.tsx index e711df6a2d3..72b3b06ba67 100644 --- a/public/app/plugins/panel/singlestat2/SingleStatValueEditor.tsx +++ b/public/app/plugins/panel/singlestat2/SingleStatValueEditor.tsx @@ -2,25 +2,11 @@ import React, { PureComponent } from 'react'; // Components -import { FormField, FormLabel, PanelOptionsGroup, Select, UnitPicker } from '@grafana/ui'; +import { FormField, FormLabel, PanelOptionsGroup, StatsPicker, UnitPicker, StatID } from '@grafana/ui'; // Types import { SingleStatValueOptions } from './types'; -const statOptions = [ - { value: 'min', label: 'Min' }, - { value: 'max', label: 'Max' }, - { value: 'avg', label: 'Average' }, - { value: 'current', label: 'Current' }, - { value: 'total', label: 'Total' }, - { value: 'name', label: 'Name' }, - { value: 'first', label: 'First' }, - { value: 'delta', label: 'Delta' }, - { value: 'diff', label: 'Difference' }, - { value: 'range', label: 'Range' }, - { value: 'last_time', label: 'Time of last point' }, -]; - const labelWidth = 6; export interface Props { @@ -30,7 +16,12 @@ export interface Props { export class SingleStatValueEditor extends PureComponent { onUnitChange = unit => this.props.onChange({ ...this.props.options, unit: unit.value }); - onStatChange = stat => this.props.onChange({ ...this.props.options, stat: stat.value }); + + onStatsChange = stats => { + console.log('SELECTED', stats); + const stat = stats[0] || StatID.mean; + this.props.onChange({ ...this.props.options, stat }); + }; onDecimalChange = event => { if (!isNaN(event.target.value)) { @@ -57,15 +48,19 @@ export class SingleStatValueEditor extends PureComponent { decimalsString = decimals.toString(); } + console.log('xxx', stat); + return (
Stat -