prometheus/web/ui/react-app/src/GraphControls.tsx
Julius Volz bca6e90ea6
Integrate beginning of React UI (#5694)
* Initial commit from Create React App

Signed-off-by: Julius Volz <julius.volz@gmail.com>

* Initial Prometheus expression browser code

Signed-off-by: Julius Volz <julius.volz@gmail.com>

* Grpahing, try out echarts

Signed-off-by: Julius Volz <julius.volz@gmail.com>

* Switch to flot

Signed-off-by: Julius Volz <julius.volz@gmail.com>

* Add metrics fetching and stuff

Signed-off-by: Julius Volz <julius.volz@gmail.com>

* Autosuggest and graph improvements

Signed-off-by: Julius Volz <julius.volz@gmail.com>

* Start implementing graph controls, add loading spinner

Signed-off-by: Julius Volz <julius.volz@gmail.com>

* So many new features and fixes

Signed-off-by: Julius Volz <julius.volz@gmail.com>

* Fixed and built more features

Signed-off-by: Julius Volz <julius.volz@gmail.com>

* Make datetimepicker clear work

Signed-off-by: Julius Volz <julius.volz@gmail.com>

* Don't abort when executing empty expression

Signed-off-by: Julius Volz <julius.volz@gmail.com>

* Remove TabPaneAlert

Signed-off-by: Julius Volz <julius.volz@gmail.com>

* Split components into separate files

Signed-off-by: Julius Volz <julius.volz@gmail.com>

* Add table time input

Signed-off-by: Julius Volz <julius.volz@gmail.com>

* Move first files to TypeScript!

Signed-off-by: Julius Volz <julius.volz@gmail.com>

* More TypeScript conversions

Signed-off-by: Julius Volz <julius.volz@gmail.com>

* More TS conversions

Signed-off-by: Julius Volz <julius.volz@gmail.com>

* More TS conversions

Signed-off-by: Julius Volz <julius.volz@gmail.com>

* More TS conversions

Signed-off-by: Julius Volz <julius.volz@gmail.com>

* More TS conversions

Signed-off-by: Julius Volz <julius.volz@gmail.com>

* More TS fixes

Signed-off-by: Julius Volz <julius.volz@gmail.com>

* Convert Graph to TS

Signed-off-by: Julius Volz <julius.volz@gmail.com>

* Changes

Signed-off-by: Julius Volz <julius.volz@gmail.com>

* Resize detector, start building legend, axis font colors

Signed-off-by: Julius Volz <julius.volz@gmail.com>

* Make graph legend work

Signed-off-by: Julius Volz <julius.volz@gmail.com>

* Add URL params support and much more

Signed-off-by: Julius Volz <julius.volz@gmail.com>

* Put panel state into panel list, write URL options

Signed-off-by: Julius Volz <julius.volz@gmail.com>

* Change order of Graph and Table tabs

Signed-off-by: Julius Volz <julius.volz@gmail.com>

* Generalize time input naming more

Signed-off-by: Julius Volz <julius.volz@gmail.com>

* Work on history functionality

Signed-off-by: Julius Volz <julius.volz@gmail.com>

* npm updates

Signed-off-by: Julius Volz <julius.volz@gmail.com>

* Move loading indicator into "Execute" button

Signed-off-by: Julius Volz <julius.volz@gmail.com>

* Fix typo

Signed-off-by: Julius Volz <julius.volz@gmail.com>

* Revert "Move loading indicator into "Execute" button"

This reverts commit ce7daee1f1af35da6c0d8b5517272839285ccfec.

Signed-off-by: Julius Volz <julius.volz@gmail.com>

* Improve error message when failing to fetch server time

Signed-off-by: Julius Volz <julius.volz@gmail.com>

* Move all code to Prometheus repo target dir

Signed-off-by: Julius Volz <julius.volz@gmail.com>

* Add react-app Makefile step and check in generated assets

Signed-off-by: Julius Volz <julius.volz@gmail.com>

* Add preliminary npm packages notice to NOTICE file

Signed-off-by: Julius Volz <julius.volz@gmail.com>

* Update React app's favicon and metadata

Signed-off-by: Julius Volz <julius.volz@gmail.com>

* Remove RP server refs, cleanups

Signed-off-by: Julius Volz <julius.volz@gmail.com>

* Use CircleCI image that includes NodeJS

Signed-off-by: Julius Volz <julius.volz@gmail.com>

* Add some missing React output assets

Signed-off-by: Julius Volz <julius.volz@gmail.com>

* Preserve CRLF in generated React files

Signed-off-by: Julius Volz <julius.volz@gmail.com>

* Switch from npm to yarn for React UI

Signed-off-by: Julius Volz <julius.volz@gmail.com>

* Save npm licenses and include them in release tarball

Signed-off-by: Julius Volz <julius.volz@gmail.com>

* Install npm on Travis

Signed-off-by: Julius Volz <julius.volz@gmail.com>

* Remove npm license tarball from source

Signed-off-by: Julius Volz <julius.volz@gmail.com>

* Remove React graph bundle from source

Signed-off-by: Julius Volz <julius.volz@gmail.com>

* Don't check in any compiled web assets

Signed-off-by: Julius Volz <julius.volz@gmail.com>

* Update README.md with node/yarn/React UI info

Signed-off-by: Julius Volz <julius.volz@gmail.com>

* Fix asset build step on CircleCI promu crossbuild

Signed-off-by: Julius Volz <julius.volz@gmail.com>

* Try to fix multi-arch go generate

Signed-off-by: Julius Volz <julius.volz@gmail.com>

* Remove check_assets from Travis CI build

Signed-off-by: Julius Volz <julius.volz@gmail.com>

* Prevent rebuilding of unchanged React app parts

Signed-off-by: Julius Volz <julius.volz@gmail.com>

* Fix npm license tarball path for promu

Signed-off-by: Julius Volz <julius.volz@gmail.com>

* Simplify Makefile

Signed-off-by: Julius Volz <julius.volz@gmail.com>

* Clarify build instructions in README.md

Signed-off-by: Julius Volz <julius.volz@gmail.com>

* Make minimal JS test pass

Signed-off-by: Julius Volz <julius.volz@gmail.com>

* Integrate React app tests into Makefile

Signed-off-by: Julius Volz <julius.volz@gmail.com>

* Separate react-app-tests target, but run it from CI

Signed-off-by: Julius Volz <julius.volz@gmail.com>

* Fix working directory for React app tests

Signed-off-by: Julius Volz <julius.volz@gmail.com>

* Remove local modifications to Makefile.common

This means that CircleCI will not run the React app tests, but at least
Travis still will...

Signed-off-by: Julius Volz <julius.volz@gmail.com>

* Depend on node_modules path for npm_licenses target

Signed-off-by: Julius Volz <julius.volz@gmail.com>

* Simplify tarball/docker/build Makefile targets

Signed-off-by: Julius Volz <julius.volz@gmail.com>

* Include React tests in "test" target

Signed-off-by: Julius Volz <julius.volz@gmail.com>

* Remove reference to removed "check_assets" target

Signed-off-by: Julius Volz <julius.volz@gmail.com>

* Do initial resize of expression input field

Signed-off-by: Julius Volz <julius.volz@gmail.com>

* Add React app proxying to local Prometheus in dev mode

Signed-off-by: Julius Volz <julius.volz@gmail.com>
2019-10-17 14:38:09 +02:00

157 lines
4.3 KiB
TypeScript

import React, { Component } from 'react';
import {
Button,
ButtonGroup,
Form,
InputGroup,
InputGroupAddon,
Input,
} from 'reactstrap';
import { library } from '@fortawesome/fontawesome-svg-core';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import {
faPlus,
faMinus,
faChartArea,
faChartLine,
} from '@fortawesome/free-solid-svg-icons';
import TimeInput from './TimeInput';
import { parseRange, formatRange } from './utils/timeFormat';
library.add(
faPlus,
faMinus,
faChartArea,
faChartLine,
);
interface GraphControlsProps {
range: number;
endTime: number | null;
resolution: number | null;
stacked: boolean;
onChangeRange: (range: number) => void;
onChangeEndTime: (endTime: number | null) => void;
onChangeResolution: (resolution: number | null) => void;
onChangeStacking: (stacked: boolean) => void;
}
class GraphControls extends Component<GraphControlsProps> {
private rangeRef = React.createRef<HTMLInputElement>();
private resolutionRef = React.createRef<HTMLInputElement>();
rangeSteps = [
1,
10,
60,
5*60,
15*60,
30*60,
60*60,
2*60*60,
6*60*60,
12*60*60,
24*60*60,
48*60*60,
7*24*60*60,
14*24*60*60,
28*24*60*60,
56*24*60*60,
365*24*60*60,
730*24*60*60,
]
onChangeRangeInput = (rangeText: string): void => {
const range = parseRange(rangeText);
if (range === null) {
this.changeRangeInput(this.props.range);
} else {
this.props.onChangeRange(range);
}
}
changeRangeInput = (range: number): void => {
this.rangeRef.current!.value = formatRange(range);
}
increaseRange = (): void => {
for (let range of this.rangeSteps) {
if (this.props.range < range) {
this.changeRangeInput(range);
this.props.onChangeRange(range);
return;
}
}
}
decreaseRange = (): void => {
for (let range of this.rangeSteps.slice().reverse()) {
if (this.props.range > range) {
this.changeRangeInput(range);
this.props.onChangeRange(range);
return;
}
}
}
componentDidUpdate(prevProps: GraphControlsProps) {
if (prevProps.range !== this.props.range) {
this.changeRangeInput(this.props.range);
}
if (prevProps.resolution !== this.props.resolution) {
this.resolutionRef.current!.value = this.props.resolution !== null ? this.props.resolution.toString() : '';
}
}
render() {
return (
<Form inline className="graph-controls" onSubmit={e => e.preventDefault()}>
<InputGroup className="range-input" size="sm">
<InputGroupAddon addonType="prepend">
<Button title="Decrease range" onClick={this.decreaseRange}><FontAwesomeIcon icon="minus" fixedWidth/></Button>
</InputGroupAddon>
<Input
defaultValue={formatRange(this.props.range)}
innerRef={this.rangeRef}
onBlur={() => this.onChangeRangeInput(this.rangeRef.current!.value)}
/>
<InputGroupAddon addonType="append">
<Button title="Increase range" onClick={this.increaseRange}><FontAwesomeIcon icon="plus" fixedWidth/></Button>
</InputGroupAddon>
</InputGroup>
<TimeInput
time={this.props.endTime}
range={this.props.range}
placeholder="End time"
onChangeTime={this.props.onChangeEndTime}
/>
<Input
placeholder="Res. (s)"
className="resolution-input"
defaultValue={this.props.resolution !== null ? this.props.resolution.toString() : ''}
innerRef={this.resolutionRef}
onBlur={() => {
const res = parseInt(this.resolutionRef.current!.value);
this.props.onChangeResolution(res ? res : null);
}}
bsSize="sm"
/>
<ButtonGroup className="stacked-input" size="sm">
<Button title="Show unstacked line graph" onClick={() => this.props.onChangeStacking(false)} active={!this.props.stacked}><FontAwesomeIcon icon="chart-line" fixedWidth/></Button>
<Button title="Show stacked graph" onClick={() => this.props.onChangeStacking(true)} active={this.props.stacked}><FontAwesomeIcon icon="chart-area" fixedWidth/></Button>
</ButtonGroup>
</Form>
);
}
}
export default GraphControls;