-
Notifications
You must be signed in to change notification settings - Fork 615
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Replace plotly line chart with pf-react area chart
- Loading branch information
1 parent
b53117a
commit e4c1f98
Showing
20 changed files
with
766 additions
and
46 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,7 @@ | ||
export { Bar } from './bar'; | ||
export { Gauge } from './gauge'; | ||
export { Line } from './line'; | ||
export { PlotlyLine } from './plotly-line'; | ||
export { QueryBrowser } from './query-browser'; | ||
export { Scalar } from './scalar'; | ||
export { Donut } from './donut'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
import * as _ from 'lodash-es'; | ||
import * as React from 'react'; | ||
import { Chart, ChartArea, ChartVoronoiContainer, ChartAxis, ChartTheme, ChartGroup } from '@patternfly/react-charts'; | ||
|
||
import { humanizeNumber } from '../utils'; | ||
import { withPrometheusWatch } from './prometheus-watch'; | ||
import { resizable } from './resizable'; | ||
import { twentyFourHourTime } from '../utils/datetime'; | ||
import { areaStyles, cartesianChartStyles } from './themes'; | ||
|
||
|
||
|
||
const LineChart = ({ | ||
data, | ||
humanize = humanizeNumber, | ||
height = 90, | ||
tickCount = 3, | ||
theme = ChartTheme.light.multi, | ||
width | ||
}) => { | ||
// Override theme. Once PF React Charts is released and we update, there should be much less we need to override here. | ||
const _theme = _.merge(theme, cartesianChartStyles, areaStyles); | ||
const getLabel = v => (data.length > 1) ? `${v.name}: ${humanize(v.y)}` : humanize(v.y); | ||
const container = <ChartVoronoiContainer voronoiDimension="x" labels={getLabel} />; | ||
return <Chart | ||
containerComponent={container} | ||
domainPadding={{y: 20}} | ||
height={height} | ||
width={width} | ||
theme={_theme} | ||
> | ||
<ChartAxis scale="time" tickCount={tickCount} tickFormat={tick => twentyFourHourTime(tick)} /> | ||
<ChartAxis dependentAxis tickCount={tickCount} tickFormat={tick => humanize(tick)} /> | ||
<ChartGroup> | ||
{ _.map(data, (values, i) => <ChartArea key={i} data={values} />) } | ||
</ChartGroup> | ||
</Chart>; | ||
} | ||
|
||
export const Line = _.flow(withPrometheusWatch,resizable)(LineChart); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,140 @@ | ||
/* eslint-disable no-undef, no-unused-vars */ | ||
import * as _ from 'lodash-es'; | ||
import * as React from 'react'; | ||
import * as classNames from 'classnames'; | ||
|
||
import { coFetchJSON } from '../../co-fetch'; | ||
import { connectToURLs, MonitoringRoutes } from '../../monitoring'; | ||
import { SafetyFirst } from '../safety-first'; | ||
import { prometheusTenancyBasePath, prometheusBasePath } from '.'; | ||
|
||
export const withPrometheusWatch = (Component) => connectToURLs(MonitoringRoutes.Prometheus)( | ||
class extends SafetyFirst<PrometheusWatchProps, PrometheusWatchState> { | ||
private interval; | ||
private timeSpan; | ||
private start; | ||
private end; | ||
constructor(props) { | ||
super(props); | ||
this.interval = null; | ||
this.timeSpan = props.timeSpan || 60 * 60 * 1000; // 1 hour | ||
this.start = null; | ||
this.end = null; | ||
this.state = { | ||
error: null, | ||
data: [], | ||
}; | ||
} | ||
|
||
fetch(enablePolling = true) { | ||
const { query, basePath, namespace, numSamples, timeout, title = ''} = this.props; | ||
const timeSpan = this.end - this.start || this.timeSpan; | ||
const end = this.end || Date.now(); | ||
const start = this.start || (end - timeSpan); | ||
const baseUrl = basePath || (namespace ? prometheusTenancyBasePath : prometheusBasePath); | ||
const pollInterval = timeSpan ? Math.max(timeSpan / 120, 3000) : 15000; | ||
const stepSize = (timeSpan && numSamples ? timeSpan / numSamples : pollInterval) / 1000; | ||
const timeoutParam = timeout ? `&timeout=${encodeURIComponent(timeout)}` : ''; | ||
const queries = !_.isArray(query) ? [{query, name: title}] : query; | ||
const promises = queries.map(q => { | ||
const nsParam = namespace ? `&namespace=${encodeURIComponent(namespace)}` : ''; | ||
const url = this.timeSpan | ||
? `${baseUrl}/api/v1/query_range?query=${encodeURIComponent(q.query)}&start=${start / 1000}&end=${end / 1000}&step=${stepSize}${nsParam}${timeoutParam}` | ||
: `${baseUrl}/api/v1/query?query=${encodeURIComponent(q.query)}${nsParam}${timeoutParam}`; | ||
return coFetchJSON(url).then(result => { | ||
const values = _.get(result, 'data.result[0].values'); | ||
return _.map(values, v => ({ | ||
name: q.name, | ||
x: new Date(v[0] * 1000), | ||
y: parseFloat(v[1]), | ||
})); | ||
}); | ||
}); | ||
Promise.all(promises) | ||
.then(data => this.setState({data})) | ||
.catch(error => this.setState({error})) | ||
.then(() => { | ||
if (enablePolling) { | ||
this.interval = setTimeout(() => { | ||
if (this.isMounted_) { | ||
this.fetch(); | ||
} | ||
}, pollInterval); | ||
} | ||
}); | ||
} | ||
|
||
componentWillMount() { | ||
if (this.props.query) { | ||
this.fetch(); | ||
} | ||
} | ||
|
||
componentWillUnmount() { | ||
super.componentWillUnmount(); | ||
clearInterval(this.interval); | ||
} | ||
|
||
prometheusURL() { | ||
const { urls, query } = this.props; | ||
const base = urls && urls[MonitoringRoutes.Prometheus]; | ||
if (!base) { | ||
return null; | ||
} | ||
|
||
const queries = _.isArray(query) ? query : [{query}]; | ||
const params = new URLSearchParams(); | ||
_.each(queries, (q, i) => { | ||
params.set(`g${i}.range_input`, '1h'); | ||
params.set(`g${i}.expr`, q.query); | ||
params.set(`g${i}.tab`, '0'); | ||
}); | ||
|
||
return `${base}/graph?${params.toString()}`; | ||
} | ||
|
||
render() { | ||
const { title, className, ...rest } = this.props; | ||
const { data } = this.state; | ||
const url = this.props.query ? this.prometheusURL() : null; | ||
|
||
return <div className={classNames('graph-wrapper', className)}> | ||
{ title && <h5 className="graph-title">{title}</h5> } | ||
{ | ||
url | ||
? <a href={url} target="_blank" rel="noopener noreferrer" style={{ textDecoration: 'none' }}> | ||
<Component {...rest} data={data} /> | ||
</a> | ||
: <Component {...rest} data={data} /> | ||
} | ||
</div>; | ||
} | ||
} | ||
); | ||
|
||
type PrometheusQuery = { | ||
name: string; | ||
query: string; | ||
}; | ||
|
||
type CartesianData = { | ||
x: Date; | ||
y: number; | ||
}; | ||
|
||
type PrometheusWatchProps = { | ||
basePath?: string; | ||
className?: string; | ||
humanize?: (v: number) => string; | ||
namespace?: string; | ||
numSamples?: number; | ||
query: PrometheusQuery[] | string; | ||
timeout?: string; | ||
title?: string; | ||
urls?: string[]; | ||
}; | ||
|
||
type PrometheusWatchState = { | ||
data: CartesianData[][]; | ||
error?: string; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.