Merge pull request #3825 from influxdata/feature/locale-number-separators

Feature/locale number separators
pull/10616/head
Andrew Watkins 2018-07-06 10:52:38 -07:00 committed by GitHub
commit 54bf8ba0ee
13 changed files with 410 additions and 407 deletions

View File

@ -1,12 +1,10 @@
import React, {Component} from 'react'
import PropTypes from 'prop-types'
import React, {PureComponent, MouseEvent} from 'react'
import {connect} from 'react-redux'
import {bindActionCreators} from 'redux'
import OptIn from 'shared/components/OptIn'
import OptIn from 'src/shared/components/OptIn'
import Input from 'src/dashboards/components/DisplayOptionsInput'
import {Tabber, Tab} from 'src/dashboards/components/Tabber'
import FancyScrollbar from 'shared/components/FancyScrollbar'
import FancyScrollbar from 'src/shared/components/FancyScrollbar'
import LineGraphColorSelector from 'src/shared/components/LineGraphColorSelector'
import {
@ -17,80 +15,51 @@ import {GRAPH_TYPES} from 'src/dashboards/graphics/graph'
import {updateAxes} from 'src/dashboards/actions/cellEditorOverlay'
import {ErrorHandling} from 'src/shared/decorators/errors'
import {Axes} from 'src/types'
const {LINEAR, LOG, BASE_2, BASE_10} = AXES_SCALE_OPTIONS
const getInputMin = scale => (scale === LOG ? '0' : null)
interface Props {
type: string
axes: Axes
staticLegend: boolean
defaultYLabel: string
handleUpdateAxes: (axes: Axes) => void
onToggleStaticLegend: (x: boolean) => (e: MouseEvent<HTMLLIElement>) => void
}
@ErrorHandling
class AxesOptions extends Component {
handleSetPrefixSuffix = e => {
const {handleUpdateAxes, axes} = this.props
const {prefix, suffix} = e.target.form
const newAxes = {
...axes,
class AxesOptions extends PureComponent<Props> {
public static defaultProps: Partial<Props> = {
axes: {
y: {
...axes.y,
prefix: prefix.value,
suffix: suffix.value,
bounds: ['', ''],
prefix: '',
suffix: '',
base: BASE_10,
scale: LINEAR,
label: '',
},
}
handleUpdateAxes(newAxes)
}
handleSetYAxisBoundMin = min => {
const {handleUpdateAxes, axes} = this.props
const {
y: {
bounds: [, max],
x: {
bounds: ['', ''],
prefix: '',
suffix: '',
base: BASE_10,
scale: LINEAR,
label: '',
},
} = this.props.axes
const newAxes = {...axes, y: {...axes.y, bounds: [min, max]}}
handleUpdateAxes(newAxes)
},
}
handleSetYAxisBoundMax = max => {
const {handleUpdateAxes, axes} = this.props
const {
y: {
bounds: [min],
},
} = axes
const newAxes = {...axes, y: {...axes.y, bounds: [min, max]}}
handleUpdateAxes(newAxes)
}
handleSetLabel = label => {
const {handleUpdateAxes, axes} = this.props
const newAxes = {...axes, y: {...axes.y, label}}
handleUpdateAxes(newAxes)
}
handleSetScale = scale => () => {
const {handleUpdateAxes, axes} = this.props
const newAxes = {...axes, y: {...axes.y, scale}}
handleUpdateAxes(newAxes)
}
handleSetBase = base => () => {
const {handleUpdateAxes, axes} = this.props
const newAxes = {...axes, y: {...axes.y, base}}
handleUpdateAxes(newAxes)
}
render() {
public render() {
const {
axes: {
y: {bounds, label, prefix, suffix, base, scale, defaultYLabel},
y: {bounds, label, prefix, suffix, base, scale},
},
type,
staticLegend,
defaultYLabel,
onToggleStaticLegend,
} = this.props
@ -109,10 +78,10 @@ class AxesOptions extends Component {
<div className="form-group col-sm-12">
<label htmlFor="prefix">Title</label>
<OptIn
customPlaceholder={defaultYLabel || 'y-axis title'}
type="text"
customValue={label}
onSetValue={this.handleSetLabel}
type="text"
customPlaceholder={defaultYLabel || 'y-axis title'}
/>
</div>
<LineGraphColorSelector />
@ -129,7 +98,7 @@ class AxesOptions extends Component {
<div className="form-group col-sm-6">
<label htmlFor="max">Max</label>
<OptIn
customPlaceholder={'max'}
customPlaceholder="max"
customValue={max}
onSetValue={this.handleSetYAxisBoundMax}
type="number"
@ -142,7 +111,6 @@ class AxesOptions extends Component {
value={prefix}
labelText="Y-Value's Prefix"
onChange={this.handleSetPrefixSuffix}
maxLength="5"
/>
<Input
name="suffix"
@ -150,11 +118,10 @@ class AxesOptions extends Component {
value={suffix}
labelText="Y-Value's Suffix"
onChange={this.handleSetPrefixSuffix}
maxLength="5"
/>
<Tabber
labelText="Y-Value's Format"
tipID="Y-Values's Format"
tipID="Y-Value's Format"
tipContent={TOOLTIP_Y_VALUE_FORMAT}
>
<Tab
@ -202,38 +169,74 @@ class AxesOptions extends Component {
</FancyScrollbar>
)
}
private handleSetPrefixSuffix = e => {
const {handleUpdateAxes, axes} = this.props
const {prefix, suffix} = e.target.form
const newAxes = {
...axes,
y: {
...axes.y,
prefix: prefix.value,
suffix: suffix.value,
},
}
handleUpdateAxes(newAxes)
}
private handleSetYAxisBoundMin = (min: string): void => {
const {handleUpdateAxes, axes} = this.props
const {
y: {
bounds: [, max],
},
} = this.props.axes
const bounds: [string, string] = [min, max]
const newAxes = {...axes, y: {...axes.y, bounds}}
handleUpdateAxes(newAxes)
}
private handleSetYAxisBoundMax = (max: string): void => {
const {handleUpdateAxes, axes} = this.props
const {
y: {
bounds: [min],
},
} = axes
const bounds: [string, string] = [min, max]
const newAxes = {...axes, y: {...axes.y, bounds}}
handleUpdateAxes(newAxes)
}
private handleSetLabel = label => {
const {handleUpdateAxes, axes} = this.props
const newAxes = {...axes, y: {...axes.y, label}}
handleUpdateAxes(newAxes)
}
private handleSetScale = scale => () => {
const {handleUpdateAxes, axes} = this.props
const newAxes = {...axes, y: {...axes.y, scale}}
handleUpdateAxes(newAxes)
}
private handleSetBase = base => () => {
const {handleUpdateAxes, axes} = this.props
const newAxes = {...axes, y: {...axes.y, base}}
handleUpdateAxes(newAxes)
}
}
const {arrayOf, bool, func, shape, string} = PropTypes
AxesOptions.defaultProps = {
axes: {
y: {
bounds: ['', ''],
prefix: '',
suffix: '',
base: BASE_10,
scale: LINEAR,
defaultYLabel: '',
},
},
}
AxesOptions.propTypes = {
type: string.isRequired,
axes: shape({
y: shape({
bounds: arrayOf(string),
label: string,
defaultYLabel: string,
}),
}).isRequired,
onToggleStaticLegend: func.isRequired,
staticLegend: bool,
handleUpdateAxes: func.isRequired,
}
const mapStateToProps = ({
const mstp = ({
cellEditorOverlay: {
cell: {axes, type},
},
@ -242,8 +245,8 @@ const mapStateToProps = ({
type,
})
const mapDispatchToProps = dispatch => ({
handleUpdateAxes: bindActionCreators(updateAxes, dispatch),
})
const mdtp = {
handleUpdateAxes: updateAxes,
}
export default connect(mapStateToProps, mapDispatchToProps)(AxesOptions)
export default connect(mstp, mdtp)(AxesOptions)

View File

@ -1,110 +0,0 @@
import React, {Component} from 'react'
import PropTypes from 'prop-types'
import {connect} from 'react-redux'
import GraphTypeSelector from 'src/dashboards/components/GraphTypeSelector'
import GaugeOptions from 'src/dashboards/components/GaugeOptions'
import SingleStatOptions from 'src/dashboards/components/SingleStatOptions'
import AxesOptions from 'src/dashboards/components/AxesOptions'
import TableOptions from 'src/dashboards/components/TableOptions'
import {buildDefaultYLabel} from 'shared/presenters'
import {ErrorHandling} from 'src/shared/decorators/errors'
@ErrorHandling
class DisplayOptions extends Component {
constructor(props) {
super(props)
const {axes, queryConfigs} = props
this.state = {
axes: this.setDefaultLabels(axes, queryConfigs),
}
}
componentWillReceiveProps(nextProps) {
const {axes, queryConfigs} = nextProps
this.setState({axes: this.setDefaultLabels(axes, queryConfigs)})
}
setDefaultLabels(axes, queryConfigs) {
return queryConfigs.length
? {
...axes,
y: {...axes.y, defaultYLabel: buildDefaultYLabel(queryConfigs[0])},
}
: axes
}
renderOptions = () => {
const {
cell: {type},
staticLegend,
onToggleStaticLegend,
onResetFocus,
queryConfigs,
} = this.props
switch (type) {
case 'gauge':
return <GaugeOptions onResetFocus={onResetFocus} />
case 'single-stat':
return <SingleStatOptions onResetFocus={onResetFocus} />
case 'table':
return (
<TableOptions
onResetFocus={onResetFocus}
queryConfigs={queryConfigs}
/>
)
default:
return (
<AxesOptions
onToggleStaticLegend={onToggleStaticLegend}
staticLegend={staticLegend}
/>
)
}
}
render() {
return (
<div className="display-options">
<GraphTypeSelector />
{this.renderOptions()}
</div>
)
}
}
const {arrayOf, bool, func, shape, string} = PropTypes
DisplayOptions.propTypes = {
cell: shape({
type: string.isRequired,
}).isRequired,
axes: shape({
y: shape({
bounds: arrayOf(string),
label: string,
defaultYLabel: string,
}),
}).isRequired,
queryConfigs: arrayOf(shape()).isRequired,
onToggleStaticLegend: func.isRequired,
staticLegend: bool,
onResetFocus: func.isRequired,
}
const mapStateToProps = ({
cellEditorOverlay: {
cell,
cell: {axes},
},
}) => ({
cell,
axes,
})
export default connect(mapStateToProps, null)(DisplayOptions)

View File

@ -0,0 +1,104 @@
import React, {PureComponent} from 'react'
import {connect} from 'react-redux'
import _ from 'lodash'
import GraphTypeSelector from 'src/dashboards/components/GraphTypeSelector'
import GaugeOptions from 'src/dashboards/components/GaugeOptions'
import SingleStatOptions from 'src/dashboards/components/SingleStatOptions'
import AxesOptions from 'src/dashboards/components/AxesOptions'
import TableOptions from 'src/dashboards/components/TableOptions'
import {buildDefaultYLabel} from 'src/shared/presenters'
import {ErrorHandling} from 'src/shared/decorators/errors'
import {Axes, Cell, QueryConfig} from 'src/types'
interface Props {
cell: Cell
Axes: Axes
queryConfigs: QueryConfig[]
staticLegend: boolean
onResetFocus: () => void
onToggleStaticLegend: (x: boolean) => () => void
}
interface State {
defaultYLabel: string
}
@ErrorHandling
class DisplayOptions extends PureComponent<Props, State> {
constructor(props) {
super(props)
this.state = {
defaultYLabel: this.defaultYLabel,
}
}
public componentDidUpdate(prevProps) {
const {queryConfigs} = prevProps
if (!_.isEqual(queryConfigs[0], this.props.queryConfigs[0])) {
this.setState({defaultYLabel: this.defaultYLabel})
}
}
public render() {
return (
<div className="display-options">
<GraphTypeSelector />
{this.renderOptions}
</div>
)
}
private get renderOptions(): JSX.Element {
const {
cell,
staticLegend,
onToggleStaticLegend,
onResetFocus,
queryConfigs,
} = this.props
const {defaultYLabel} = this.state
switch (cell.type) {
case 'gauge':
return <GaugeOptions onResetFocus={onResetFocus} />
case 'single-stat':
return <SingleStatOptions onResetFocus={onResetFocus} />
case 'table':
return (
<TableOptions
onResetFocus={onResetFocus}
queryConfigs={queryConfigs}
/>
)
default:
return (
<AxesOptions
staticLegend={staticLegend}
defaultYLabel={defaultYLabel}
onToggleStaticLegend={onToggleStaticLegend}
/>
)
}
}
private get defaultYLabel(): string {
const {queryConfigs} = this.props
if (queryConfigs.length) {
return buildDefaultYLabel(queryConfigs[0])
}
return ''
}
}
const mstp = ({cellEditorOverlay}) => ({
cell: cellEditorOverlay.cell,
axes: cellEditorOverlay.cell.axes,
})
export default connect(mstp, null)(DisplayOptions)

View File

@ -1,7 +1,16 @@
import React from 'react'
import PropTypes from 'prop-types'
import React, {SFC, ChangeEvent} from 'react'
const DisplayOptionsInput = ({
interface Props {
name: string
id: string
value: string
onChange: (e: ChangeEvent<HTMLInputElement>) => void
labelText?: string
colWidth?: string
placeholder?: string
}
const DisplayOptionsInput: SFC<Props> = ({
id,
name,
value,
@ -24,22 +33,10 @@ const DisplayOptionsInput = ({
</div>
)
const {func, string} = PropTypes
DisplayOptionsInput.defaultProps = {
value: '',
colWidth: 'col-sm-6',
placeholder: '',
}
DisplayOptionsInput.propTypes = {
name: string.isRequired,
id: string.isRequired,
value: string.isRequired,
onChange: func.isRequired,
labelText: string,
colWidth: string,
placeholder: string,
}
export default DisplayOptionsInput

View File

@ -1,32 +1,23 @@
import React, {Component} from 'react'
import PropTypes from 'prop-types'
import React, {PureComponent} from 'react'
import {connect} from 'react-redux'
import {bindActionCreators} from 'redux'
import FancyScrollbar from 'shared/components/FancyScrollbar'
import ThresholdsList from 'shared/components/ThresholdsList'
import ThresholdsListTypeToggle from 'shared/components/ThresholdsListTypeToggle'
import FancyScrollbar from 'src/shared/components/FancyScrollbar'
import ThresholdsList from 'src/shared/components/ThresholdsList'
import ThresholdsListTypeToggle from 'src/shared/components/ThresholdsListTypeToggle'
import {updateAxes} from 'src/dashboards/actions/cellEditorOverlay'
import {ErrorHandling} from 'src/shared/decorators/errors'
import {Axes} from 'src/types'
interface Props {
handleUpdateAxes: (axes: Axes) => void
axes: Axes
onResetFocus: () => void
}
@ErrorHandling
class SingleStatOptions extends Component {
handleUpdatePrefix = e => {
const {handleUpdateAxes, axes} = this.props
const newAxes = {...axes, y: {...axes.y, prefix: e.target.value}}
handleUpdateAxes(newAxes)
}
handleUpdateSuffix = e => {
const {handleUpdateAxes, axes} = this.props
const newAxes = {...axes, y: {...axes.y, suffix: e.target.value}}
handleUpdateAxes(newAxes)
}
render() {
class SingleStatOptions extends PureComponent<Props> {
public render() {
const {
axes: {
y: {prefix, suffix},
@ -50,7 +41,7 @@ class SingleStatOptions extends Component {
placeholder="%, MPH, etc."
defaultValue={prefix}
onChange={this.handleUpdatePrefix}
maxLength="5"
maxLength={5}
/>
</div>
<div className="form-group col-xs-6">
@ -60,7 +51,7 @@ class SingleStatOptions extends Component {
placeholder="%, MPH, etc."
defaultValue={suffix}
onChange={this.handleUpdateSuffix}
maxLength="5"
maxLength={5}
/>
</div>
<ThresholdsListTypeToggle containerClass="form-group col-xs-6" />
@ -69,26 +60,28 @@ class SingleStatOptions extends Component {
</FancyScrollbar>
)
}
private handleUpdatePrefix = e => {
const {handleUpdateAxes, axes} = this.props
const newAxes = {...axes, y: {...axes.y, prefix: e.target.value}}
handleUpdateAxes(newAxes)
}
private handleUpdateSuffix = e => {
const {handleUpdateAxes, axes} = this.props
const newAxes = {...axes, y: {...axes.y, suffix: e.target.value}}
handleUpdateAxes(newAxes)
}
}
const {func, shape} = PropTypes
const mstp = ({cellEditorOverlay}) => ({
axes: cellEditorOverlay.cell.axes,
})
SingleStatOptions.propTypes = {
handleUpdateAxes: func.isRequired,
axes: shape({}).isRequired,
onResetFocus: func.isRequired,
const mdtp = {
handleUpdateAxes: updateAxes,
}
const mapStateToProps = ({
cellEditorOverlay: {
cell: {axes},
},
}) => ({
axes,
})
const mapDispatchToProps = dispatch => ({
handleUpdateAxes: bindActionCreators(updateAxes, dispatch),
})
export default connect(mapStateToProps, mapDispatchToProps)(SingleStatOptions)
export default connect(mstp, mdtp)(SingleStatOptions)

View File

@ -1,36 +0,0 @@
import React from 'react'
import PropTypes from 'prop-types'
import QuestionMarkTooltip from 'src/shared/components/QuestionMarkTooltip'
export const Tabber = ({labelText, children, tipID, tipContent}) => (
<div className="form-group col-md-6">
<label>
{labelText}
{tipID ? (
<QuestionMarkTooltip tipID={tipID} tipContent={tipContent} />
) : null}
</label>
<ul className="nav nav-tablist nav-tablist-sm">{children}</ul>
</div>
)
export const Tab = ({isActive, onClickTab, text}) => (
<li className={isActive ? 'active' : ''} onClick={onClickTab}>
{text}
</li>
)
const {bool, func, node, string} = PropTypes
Tabber.propTypes = {
children: node.isRequired,
labelText: string,
tipID: string,
tipContent: string,
}
Tab.propTypes = {
onClickTab: func.isRequired,
isActive: bool.isRequired,
text: string.isRequired,
}

View File

@ -0,0 +1,36 @@
import React, {SFC, MouseEvent} from 'react'
import QuestionMarkTooltip from 'src/shared/components/QuestionMarkTooltip'
interface TabberProps {
labelText?: string
children: JSX.Element[]
tipID?: string
tipContent?: string
}
export const Tabber: SFC<TabberProps> = ({
children,
tipID = '',
labelText = '',
tipContent = '',
}) => (
<div className="form-group col-md-6">
<label>
{labelText}
{!!tipID && <QuestionMarkTooltip tipID={tipID} tipContent={tipContent} />}
</label>
<ul className="nav nav-tablist nav-tablist-sm">{children}</ul>
</div>
)
interface TabProps {
onClickTab: (e: MouseEvent<HTMLLIElement>) => void
isActive: boolean
text: string
}
export const Tab: SFC<TabProps> = ({isActive, onClickTab, text}) => (
<li className={isActive ? 'active' : ''} onClick={onClickTab}>
{text}
</li>
)

View File

@ -4,7 +4,7 @@ import onClickOutside from 'src/shared/components/OnClickOutside'
import {ErrorHandling} from 'src/shared/decorators/errors'
interface Props {
min: string
min?: string
id: string
type: string
customPlaceholder: string
@ -25,7 +25,7 @@ class ClickOutsideInput extends Component<Props> {
public render() {
const {
id,
min,
min = '',
type,
onFocus,
onChange,

View File

@ -290,7 +290,7 @@ class Gauge extends Component {
ctx.textAlign = 'center'
const textY = radius
const textContent = `${prefix}${gaugePosition.toString()}${suffix}`
const textContent = `${prefix}${gaugePosition.toLocaleString()}${suffix}`
ctx.fillText(textContent, 0, textY)
}

View File

@ -1,44 +1,82 @@
import React, {PureComponent, CSSProperties} from 'react'
import Dygraph from 'src/shared/components/Dygraph'
import _ from 'lodash'
import React, {Component} from 'react'
import PropTypes from 'prop-types'
import Dygraph from 'shared/components/Dygraph'
import SingleStat from 'src/shared/components/SingleStat'
import {timeSeriesToDygraph} from 'utils/timeSeriesTransformers'
import {
timeSeriesToDygraph,
TimeSeriesToDyGraphReturnType,
} from 'src/utils/timeSeriesTransformers'
import {colorsStringSchema} from 'shared/schemas'
import {ErrorHandlingWith} from 'src/shared/decorators/errors'
import InvalidData from 'src/shared/components/InvalidData'
import {Query, Axes, RuleValues, TimeRange} from 'src/types'
import {ColorString} from 'src/types/colors'
import {Data} from 'src/types/dygraphs'
const validateTimeSeries = timeseries => {
return _.every(timeseries, r =>
const validateTimeSeries = ts => {
return _.every(ts, r =>
_.every(
r,
(v, i) => (i === 0 && Date.parse(v)) || _.isNumber(v) || _.isNull(v)
(v, i: number) =>
(i === 0 && Date.parse(v)) || _.isNumber(v) || _.isNull(v)
)
)
}
interface Props {
axes: Axes
title: string
cellID: string
cellHeight: number
isFetchingInitially: boolean
isRefreshing: boolean
isGraphFilled: boolean
isBarGraph: boolean
staticLegend: boolean
showSingleStat: boolean
displayOptions: {
stepPlot: boolean
stackedGraph: boolean
animatedZooms: boolean
}
activeQueryIndex: number
ruleValues: RuleValues
timeRange: TimeRange
isInDataExplorer: boolean
onZoom: () => void
data: Data
queries: Query[]
colors: ColorString[]
underlayCallback?: () => void
setResolution: () => void
handleSetHoverTime: () => void
}
@ErrorHandlingWith(InvalidData)
class LineGraph extends Component {
constructor(props) {
super(props)
this.isValidData = true
class LineGraph extends PureComponent<Props> {
public static defaultProps: Partial<Props> = {
underlayCallback: () => {},
isGraphFilled: true,
staticLegend: false,
}
componentWillMount() {
private isValidData: boolean = true
private timeSeries: TimeSeriesToDyGraphReturnType
public componentWillMount() {
const {data, isInDataExplorer} = this.props
this.parseTimeSeries(data, isInDataExplorer)
}
parseTimeSeries(data, isInDataExplorer) {
this._timeSeries = timeSeriesToDygraph(data, isInDataExplorer)
public parseTimeSeries(data, isInDataExplorer) {
this.timeSeries = timeSeriesToDygraph(data, isInDataExplorer)
this.isValidData = validateTimeSeries(
_.get(this._timeSeries, 'timeSeries', [])
_.get(this.timeSeries, 'timeSeries', [])
)
}
componentWillUpdate(nextProps) {
public componentWillUpdate(nextProps) {
const {data, activeQueryIndex} = this.props
if (
data !== nextProps.data ||
@ -48,7 +86,7 @@ class LineGraph extends Component {
}
}
render() {
public render() {
if (!this.isValidData) {
return <InvalidData />
}
@ -76,7 +114,7 @@ class LineGraph extends Component {
handleSetHoverTime,
} = this.props
const {labels, timeSeries, dygraphSeries} = this._timeSeries
const {labels, timeSeries, dygraphSeries} = this.timeSeries
// If data for this graph is being fetched for the first time, show a graph-wide spinner.
if (isFetchingInitially) {
@ -99,19 +137,9 @@ class LineGraph extends Component {
connectSeparatedPoints: true,
}
const containerStyle = {
width: 'calc(100% - 32px)',
height: 'calc(100% - 16px)',
position: 'absolute',
top: '8px',
}
const prefix = axes ? axes.y.prefix : ''
const suffix = axes ? axes.y.suffix : ''
return (
<div className="dygraph graph--hasYLabel" style={{height: '100%'}}>
{isRefreshing ? <GraphLoadingDots /> : null}
<div className="dygraph graph--hasYLabel" style={this.style}>
{isRefreshing && <GraphLoadingDots />}
<Dygraph
axes={axes}
cellID={cellID}
@ -124,27 +152,61 @@ class LineGraph extends Component {
isBarGraph={isBarGraph}
timeSeries={timeSeries}
ruleValues={ruleValues}
staticLegend={staticLegend}
dygraphSeries={dygraphSeries}
setResolution={setResolution}
containerStyle={this.containerStyle}
handleSetHoverTime={handleSetHoverTime}
containerStyle={containerStyle}
staticLegend={staticLegend}
isGraphFilled={showSingleStat ? false : isGraphFilled}
>
{showSingleStat && (
<SingleStat
prefix={prefix}
suffix={suffix}
data={data}
lineGraph={true}
colors={colors}
prefix={this.prefix}
suffix={this.suffix}
cellHeight={cellHeight}
isFetchingInitially={isFetchingInitially}
/>
)}
</Dygraph>
</div>
)
}
private get style(): CSSProperties {
return {height: '100%'}
}
private get prefix(): string {
const {axes} = this.props
if (!axes) {
return ''
}
return axes.y.prefix
}
private get suffix(): string {
const {axes} = this.props
if (!axes) {
return ''
}
return axes.y.suffix
}
private get containerStyle(): CSSProperties {
return {
width: 'calc(100% - 32px)',
height: 'calc(100% - 16px)',
position: 'absolute',
top: '8px',
}
}
}
const GraphLoadingDots = () => (
@ -161,52 +223,4 @@ const GraphSpinner = () => (
</div>
)
const {array, arrayOf, bool, func, number, shape, string} = PropTypes
LineGraph.defaultProps = {
underlayCallback: () => {},
isGraphFilled: true,
staticLegend: false,
}
LineGraph.propTypes = {
cellID: string,
axes: shape({
y: shape({
bounds: array,
label: string,
}),
y2: shape({
bounds: array,
label: string,
}),
}),
handleSetHoverTime: func,
title: string,
isFetchingInitially: bool,
isRefreshing: bool,
underlayCallback: func,
isGraphFilled: bool,
isBarGraph: bool,
staticLegend: bool,
showSingleStat: bool,
displayOptions: shape({
stepPlot: bool,
stackedGraph: bool,
animatedZooms: bool,
}),
activeQueryIndex: number,
ruleValues: shape({}),
timeRange: shape({
lower: string.isRequired,
}),
isInDataExplorer: bool,
setResolution: func,
cellHeight: number,
onZoom: func,
queries: arrayOf(shape({}).isRequired).isRequired,
data: arrayOf(shape({}).isRequired).isRequired,
colors: colorsStringSchema,
}
export default LineGraph

View File

@ -7,11 +7,11 @@ import ClickOutsideInput from 'src/shared/components/ClickOutsideInput'
import {ErrorHandling} from 'src/shared/decorators/errors'
interface Props {
min: string
fixedPlaceholder: string
fixedValue: string
customPlaceholder: string
customValue: string
min?: string
fixedPlaceholder?: string
fixedValue?: string
customPlaceholder?: string
customValue?: string
onSetValue: (value: string) => void
type: string | number
}
@ -25,10 +25,11 @@ interface State {
@ErrorHandling
export default class OptIn extends Component<Props, State> {
public static defaultProps: Partial<Props> = {
min: '',
fixedValue: '',
customPlaceholder: 'Custom Value',
fixedPlaceholder: 'auto',
customValue: '',
fixedPlaceholder: 'auto',
customPlaceholder: 'Custom Value',
}
private id: string

View File

@ -6,7 +6,7 @@ import _ from 'lodash'
import {SMALL_CELL_HEIGHT} from 'src/shared/graphs/helpers'
import {DYGRAPH_CONTAINER_V_MARGIN} from 'src/shared/constants'
import {generateThresholdsListHexs} from 'src/shared/constants/colorOperations'
import {ColorNumber} from 'src/types/colors'
import {ColorString} from 'src/types/colors'
import {CellType} from 'src/types/dashboards'
import {Data} from 'src/types/dygraphs'
import {ErrorHandling} from 'src/shared/decorators/errors'
@ -14,11 +14,11 @@ import {ErrorHandling} from 'src/shared/decorators/errors'
interface Props {
isFetchingInitially: boolean
cellHeight: number
colors: ColorNumber[]
colors: ColorString[]
prefix?: string
suffix?: string
lineGraph: boolean
staticLegendHeight: number
staticLegendHeight?: number
data: Data
}
@ -71,8 +71,9 @@ class SingleStat extends PureComponent<Props> {
const lastValue = lastValues[firstAlphabeticalindex]
const HUNDRED = 100.0
const roundedValue = Math.round(+lastValue * HUNDRED) / HUNDRED
const localeFormatted = roundedValue.toLocaleString()
return `${roundedValue}`
return `${localeFormatted}`
}
private get containerStyle(): CSSProperties {
@ -101,11 +102,11 @@ class SingleStat extends PureComponent<Props> {
const {lastValues, series} = getLastValues(data)
const firstAlphabeticalSeriesName = _.sortBy(series)[0]
const firstAlphabeticalindex = _.indexOf(
const firstAlphabeticalIndex = _.indexOf(
series,
firstAlphabeticalSeriesName
)
const lastValue = lastValues[firstAlphabeticalindex]
const lastValue = lastValues[firstAlphabeticalIndex]
const {bgColor, textColor} = generateThresholdsListHexs({
colors,

View File

@ -14,7 +14,7 @@ interface Label {
responseIndex: number
}
interface TimeSeriesToDyGraphReturnType {
export interface TimeSeriesToDyGraphReturnType {
labels: string[]
timeSeries: DygraphValue[][]
dygraphSeries: DygraphSeries