diff --git a/packages/grafana-ui/src/components/StatsPicker/StatsPicker.story.tsx b/packages/grafana-ui/src/components/StatsPicker/StatsPicker.story.tsx new file mode 100644 index 00000000000..c8ea6eb0f50 --- /dev/null +++ b/packages/grafana-ui/src/components/StatsPicker/StatsPicker.story.tsx @@ -0,0 +1,79 @@ +import React, { PureComponent } from 'react'; + +import { storiesOf } from '@storybook/react'; +import { action } from '@storybook/addon-actions'; +import { withCenteredStory } from '../../utils/storybook/withCenteredStory'; +import { StatsPicker } from './StatsPicker'; +import { text, boolean } from '@storybook/addon-knobs'; + +const getKnobs = () => { + return { + placeholder: text('Placeholder Text', ''), + defaultStat: text('Default Stat', ''), + allowMultiple: boolean('Allow Multiple', false), + initialStats: text('Initial Stats', ''), + }; +}; + +interface State { + stats: string[]; +} + +export class WrapperWithState extends PureComponent { + constructor(props: any) { + super(props); + this.state = { + stats: this.toStatsArray(props.initialReducers), + }; + } + + toStatsArray = (txt: string): string[] => { + if (!txt) { + return []; + } + return txt.split(',').map(v => v.trim()); + }; + + componentDidUpdate(prevProps: any) { + const { initialReducers } = this.props; + if (initialReducers !== prevProps.initialReducers) { + console.log('Changing initial reducers'); + this.setState({ stats: this.toStatsArray(initialReducers) }); + } + } + + render() { + const { placeholder, defaultStat, allowMultiple } = this.props; + const { stats } = this.state; + + return ( + { + action('Picked:')(stats); + this.setState({ stats }); + }} + /> + ); + } +} + +const story = storiesOf('UI/StatsPicker', module); +story.addDecorator(withCenteredStory); +story.add('picker', () => { + const { placeholder, defaultStat, allowMultiple, initialStats } = getKnobs(); + + return ( +
+ +
+ ); +}); diff --git a/packages/grafana-ui/src/components/StatsPicker/StatsPicker.tsx b/packages/grafana-ui/src/components/StatsPicker/StatsPicker.tsx new file mode 100644 index 00000000000..d15c7ebfcd8 --- /dev/null +++ b/packages/grafana-ui/src/components/StatsPicker/StatsPicker.tsx @@ -0,0 +1,98 @@ +import React, { PureComponent } from 'react'; + +import isArray from 'lodash/isArray'; +import difference from 'lodash/difference'; + +import { Select } from '../index'; + +import { getStatsCalculators } from '../../utils/statsCalculator'; +import { SelectOptionItem } from '../Select/Select'; + +interface Props { + placeholder?: string; + onChange: (stats: string[]) => void; + stats: string[]; + width?: number; + allowMultiple?: boolean; + defaultStat?: string; +} + +export class StatsPicker extends PureComponent { + static defaultProps = { + width: 12, + allowMultiple: false, + }; + + componentDidMount() { + this.checkInput(); + } + + componentDidUpdate(prevProps: Props) { + this.checkInput(); + } + + checkInput = () => { + const { stats, allowMultiple, defaultStat, onChange } = this.props; + + const current = getStatsCalculators(stats); + if (current.length !== stats.length) { + const found = current.map(v => v.id); + const notFound = difference(stats, found); + console.warn('Unknown stats', notFound, stats); + onChange(current.map(stat => stat.id)); + } + + // Make sure there is only one + if (!allowMultiple && stats.length > 1) { + console.warn('Removing extra stat', stats); + onChange([stats[0]]); + } + + // Set the reducer from callback + if (defaultStat && stats.length < 1) { + onChange([defaultStat]); + } + }; + + onSelectionChange = (item: SelectOptionItem) => { + const { onChange } = this.props; + if (isArray(item)) { + onChange(item.map(v => v.value)); + } else { + onChange([item.value]); + } + }; + + render() { + const { width, stats, allowMultiple, defaultStat, placeholder } = this.props; + const options = getStatsCalculators().map(s => { + return { + value: s.id, + label: s.name, + description: s.description, + }; + }); + + const value: SelectOptionItem[] = []; + stats.forEach(s => { + const o = options.find(v => v.value === s); + if (o) { + value.push(o); + } + }); + + //getStatsCalculators(stats); + return ( + option.value === stat)} + placeholder="Choose Stat" + defaultStat={StatID.mean} + allowMultiple={false} + stats={[stat]} + onChange={this.onStatsChange} />
diff --git a/public/app/plugins/panel/singlestat2/module.tsx b/public/app/plugins/panel/singlestat2/module.tsx index 283b32802e1..8c86b3497ee 100644 --- a/public/app/plugins/panel/singlestat2/module.tsx +++ b/public/app/plugins/panel/singlestat2/module.tsx @@ -1,4 +1,4 @@ -import { ReactPanelPlugin } from '@grafana/ui'; +import { ReactPanelPlugin, getStatsCalculators } from '@grafana/ui'; import { SingleStatOptions, defaults, SingleStatBaseOptions } from './types'; import { SingleStatPanel } from './SingleStatPanel'; import cloneDeep from 'lodash/cloneDeep'; @@ -21,6 +21,13 @@ export const singleStatBaseOptionsCheck = ( }); } + // 6.1 renamed some stats, This makes sure they are up to date + // avg -> mean, current -> last, total -> sum + const { valueOptions } = options; + if (valueOptions && valueOptions.stat) { + valueOptions.stat = getStatsCalculators([valueOptions.stat]).map(s => s.id)[0]; + console.log('CHANGED', valueOptions); + } return options; }; diff --git a/public/app/plugins/panel/singlestat2/types.ts b/public/app/plugins/panel/singlestat2/types.ts index 1f31783e814..797e0cb0116 100644 --- a/public/app/plugins/panel/singlestat2/types.ts +++ b/public/app/plugins/panel/singlestat2/types.ts @@ -1,4 +1,4 @@ -import { VizOrientation, ValueMapping, Threshold } from '@grafana/ui'; +import { VizOrientation, ValueMapping, Threshold, StatID } from '@grafana/ui'; export interface SingleStatBaseOptions { valueMappings: ValueMapping[]; @@ -24,7 +24,7 @@ export const defaults: SingleStatOptions = { prefix: '', suffix: '', decimals: null, - stat: 'avg', + stat: StatID.mean, unit: 'none', }, valueMappings: [],