mirror of
https://github.com/prometheus/prometheus.git
synced 2025-03-05 20:59:13 -08:00
Autosuggest and graph improvements
Signed-off-by: Julius Volz <julius.volz@gmail.com>
This commit is contained in:
parent
9a910701fa
commit
00d3821218
171
package-lock.json
generated
171
package-lock.json
generated
|
@ -3077,6 +3077,11 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"compute-scroll-into-view": {
|
||||
"version": "1.0.11",
|
||||
"resolved": "https://registry.npmjs.org/compute-scroll-into-view/-/compute-scroll-into-view-1.0.11.tgz",
|
||||
"integrity": "sha512-uUnglJowSe0IPmWOdDtrlHXof5CTIJitfJEyITHBW6zDVOGu9Pjk5puaLM73SLcwak0L4hEjO7Td88/a6P5i7A=="
|
||||
},
|
||||
"concat-map": {
|
||||
"version": "0.0.1",
|
||||
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
|
||||
|
@ -4065,6 +4070,17 @@
|
|||
"resolved": "https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-4.2.0.tgz",
|
||||
"integrity": "sha1-3vHxyl1gWdJKdm5YeULCEQbOEnU="
|
||||
},
|
||||
"downshift": {
|
||||
"version": "3.2.2",
|
||||
"resolved": "https://registry.npmjs.org/downshift/-/downshift-3.2.2.tgz",
|
||||
"integrity": "sha512-z4rkPnfC/ax1LnZkipQOlEu8WSDKduPudRjurlU85Qxy39eeY1ArAi0xU24qXcxd27bMS81mBMgkH7X/pEH2GA==",
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.1.2",
|
||||
"compute-scroll-into-view": "^1.0.9",
|
||||
"prop-types": "^15.6.0",
|
||||
"react-is": "^16.5.2"
|
||||
}
|
||||
},
|
||||
"duplexer": {
|
||||
"version": "0.1.1",
|
||||
"resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.1.tgz",
|
||||
|
@ -6304,6 +6320,46 @@
|
|||
"requires": {
|
||||
"h2x-types": "^1.1.0",
|
||||
"jsdom": ">=11.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"jsdom": {
|
||||
"version": "13.2.0",
|
||||
"resolved": "https://registry.npmjs.org/jsdom/-/jsdom-13.2.0.tgz",
|
||||
"integrity": "sha512-cG1NtMWO9hWpqRNRR3dSvEQa8bFI6iLlqU2x4kwX51FQjp0qus8T9aBaAO6iGp3DeBrhdwuKxckknohkmfvsFw==",
|
||||
"requires": {
|
||||
"abab": "^2.0.0",
|
||||
"acorn": "^6.0.4",
|
||||
"acorn-globals": "^4.3.0",
|
||||
"array-equal": "^1.0.0",
|
||||
"cssom": "^0.3.4",
|
||||
"cssstyle": "^1.1.1",
|
||||
"data-urls": "^1.1.0",
|
||||
"domexception": "^1.0.1",
|
||||
"escodegen": "^1.11.0",
|
||||
"html-encoding-sniffer": "^1.0.2",
|
||||
"nwsapi": "^2.0.9",
|
||||
"parse5": "5.1.0",
|
||||
"pn": "^1.1.0",
|
||||
"request": "^2.88.0",
|
||||
"request-promise-native": "^1.0.5",
|
||||
"saxes": "^3.1.5",
|
||||
"symbol-tree": "^3.2.2",
|
||||
"tough-cookie": "^2.5.0",
|
||||
"w3c-hr-time": "^1.0.1",
|
||||
"w3c-xmlserializer": "^1.0.1",
|
||||
"webidl-conversions": "^4.0.2",
|
||||
"whatwg-encoding": "^1.0.5",
|
||||
"whatwg-mimetype": "^2.3.0",
|
||||
"whatwg-url": "^7.0.0",
|
||||
"ws": "^6.1.2",
|
||||
"xml-name-validator": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"parse5": {
|
||||
"version": "5.1.0",
|
||||
"resolved": "https://registry.npmjs.org/parse5/-/parse5-5.1.0.tgz",
|
||||
"integrity": "sha512-fxNG2sQjHvlVAYmzBZS9YlDp6PTSSDwa98vkD4QgVDDCAo84z5X1t5XyJQ62ImdLXx5NdIIfihey6xpum9/gRQ=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"h2x-plugin-jsx": {
|
||||
|
@ -8401,36 +8457,79 @@
|
|||
"integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM="
|
||||
},
|
||||
"jsdom": {
|
||||
"version": "13.2.0",
|
||||
"resolved": "https://registry.npmjs.org/jsdom/-/jsdom-13.2.0.tgz",
|
||||
"integrity": "sha512-cG1NtMWO9hWpqRNRR3dSvEQa8bFI6iLlqU2x4kwX51FQjp0qus8T9aBaAO6iGp3DeBrhdwuKxckknohkmfvsFw==",
|
||||
"version": "9.6.0",
|
||||
"resolved": "https://registry.npmjs.org/jsdom/-/jsdom-9.6.0.tgz",
|
||||
"integrity": "sha1-4OmxW6B+kLHZ7Ag/m+3uD2gApPs=",
|
||||
"requires": {
|
||||
"abab": "^2.0.0",
|
||||
"acorn": "^6.0.4",
|
||||
"acorn-globals": "^4.3.0",
|
||||
"abab": "^1.0.0",
|
||||
"acorn": "^2.4.0",
|
||||
"acorn-globals": "^1.0.4",
|
||||
"array-equal": "^1.0.0",
|
||||
"cssom": "^0.3.4",
|
||||
"cssstyle": "^1.1.1",
|
||||
"data-urls": "^1.1.0",
|
||||
"domexception": "^1.0.1",
|
||||
"escodegen": "^1.11.0",
|
||||
"html-encoding-sniffer": "^1.0.2",
|
||||
"nwsapi": "^2.0.9",
|
||||
"parse5": "5.1.0",
|
||||
"pn": "^1.1.0",
|
||||
"request": "^2.88.0",
|
||||
"request-promise-native": "^1.0.5",
|
||||
"saxes": "^3.1.5",
|
||||
"symbol-tree": "^3.2.2",
|
||||
"tough-cookie": "^2.5.0",
|
||||
"w3c-hr-time": "^1.0.1",
|
||||
"w3c-xmlserializer": "^1.0.1",
|
||||
"webidl-conversions": "^4.0.2",
|
||||
"whatwg-encoding": "^1.0.5",
|
||||
"whatwg-mimetype": "^2.3.0",
|
||||
"whatwg-url": "^7.0.0",
|
||||
"ws": "^6.1.2",
|
||||
"xml-name-validator": "^3.0.0"
|
||||
"cssom": ">= 0.3.0 < 0.4.0",
|
||||
"cssstyle": ">= 0.2.36 < 0.3.0",
|
||||
"escodegen": "^1.6.1",
|
||||
"iconv-lite": "^0.4.13",
|
||||
"nwmatcher": ">= 1.3.7 < 2.0.0",
|
||||
"parse5": "^1.5.1",
|
||||
"request": "^2.55.0",
|
||||
"sax": "^1.1.4",
|
||||
"symbol-tree": ">= 3.1.0 < 4.0.0",
|
||||
"tough-cookie": "^2.3.1",
|
||||
"webidl-conversions": "^3.0.1",
|
||||
"whatwg-url": "^3.0.0",
|
||||
"xml-name-validator": ">= 2.0.1 < 3.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"abab": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/abab/-/abab-1.0.4.tgz",
|
||||
"integrity": "sha1-X6rZwsB/YN12dw9xzwJbYqY8/U4="
|
||||
},
|
||||
"acorn": {
|
||||
"version": "2.7.0",
|
||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-2.7.0.tgz",
|
||||
"integrity": "sha1-q259nYhqrKiwhbwzEreaGYQz8Oc="
|
||||
},
|
||||
"acorn-globals": {
|
||||
"version": "1.0.9",
|
||||
"resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-1.0.9.tgz",
|
||||
"integrity": "sha1-VbtemGkVB7dFedBRNBMhfDgMVM8=",
|
||||
"requires": {
|
||||
"acorn": "^2.1.0"
|
||||
}
|
||||
},
|
||||
"cssstyle": {
|
||||
"version": "0.2.37",
|
||||
"resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-0.2.37.tgz",
|
||||
"integrity": "sha1-VBCXI0yyUTyDzu06zdwn/yeYfVQ=",
|
||||
"requires": {
|
||||
"cssom": "0.3.x"
|
||||
}
|
||||
},
|
||||
"tr46": {
|
||||
"version": "0.0.3",
|
||||
"resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
|
||||
"integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o="
|
||||
},
|
||||
"webidl-conversions": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
|
||||
"integrity": "sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE="
|
||||
},
|
||||
"whatwg-url": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-3.1.0.tgz",
|
||||
"integrity": "sha1-e9yuSQ+SGu9kUftnOexrvY6Qe/Y=",
|
||||
"requires": {
|
||||
"tr46": "~0.0.3",
|
||||
"webidl-conversions": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"xml-name-validator": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-2.0.1.tgz",
|
||||
"integrity": "sha1-TYuPHszTQZqjYgYb7O9RXh5VljU="
|
||||
}
|
||||
}
|
||||
},
|
||||
"jsesc": {
|
||||
|
@ -9286,6 +9385,11 @@
|
|||
"resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz",
|
||||
"integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0="
|
||||
},
|
||||
"nwmatcher": {
|
||||
"version": "1.4.4",
|
||||
"resolved": "https://registry.npmjs.org/nwmatcher/-/nwmatcher-1.4.4.tgz",
|
||||
"integrity": "sha512-3iuY4N5dhgMpCUrOVnuAdGrgxVqV2cJpM+XNccjR2DKOB1RUP0aA+wGXEiNziG/UKboFyGBIoKOaNlJxx8bciQ=="
|
||||
},
|
||||
"nwsapi": {
|
||||
"version": "2.0.9",
|
||||
"resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.0.9.tgz",
|
||||
|
@ -9608,9 +9712,9 @@
|
|||
"integrity": "sha1-bVuTSkVpk7I9N/QKOC1vFmao5cY="
|
||||
},
|
||||
"parse5": {
|
||||
"version": "5.1.0",
|
||||
"resolved": "https://registry.npmjs.org/parse5/-/parse5-5.1.0.tgz",
|
||||
"integrity": "sha512-fxNG2sQjHvlVAYmzBZS9YlDp6PTSSDwa98vkD4QgVDDCAo84z5X1t5XyJQ62ImdLXx5NdIIfihey6xpum9/gRQ=="
|
||||
"version": "1.5.1",
|
||||
"resolved": "https://registry.npmjs.org/parse5/-/parse5-1.5.1.tgz",
|
||||
"integrity": "sha1-m387DeMr543CQBsXVzzK8Pb1nZQ="
|
||||
},
|
||||
"parseurl": {
|
||||
"version": "1.3.2",
|
||||
|
@ -13678,6 +13782,11 @@
|
|||
"jquery": "^3.1.1"
|
||||
}
|
||||
},
|
||||
"react-is": {
|
||||
"version": "16.8.1",
|
||||
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.8.1.tgz",
|
||||
"integrity": "sha512-ioMCzVDWvCvKD8eeT+iukyWrBGrA3DiFYkXfBsVYIRdaREZuBjENG+KjrikavCLasozqRWTwFUagU/O4vPpRMA=="
|
||||
},
|
||||
"react-lifecycles-compat": {
|
||||
"version": "3.0.4",
|
||||
"resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz",
|
||||
|
|
|
@ -4,8 +4,9 @@
|
|||
"private": true,
|
||||
"dependencies": {
|
||||
"bootstrap": "^4.2.1",
|
||||
"downshift": "^3.2.2",
|
||||
"jquery": "^3.3.1",
|
||||
"jsdom": "^13.2.0",
|
||||
"jsdom": "^9.6.0",
|
||||
"react": "^16.7.0",
|
||||
"react-dom": "^16.7.0",
|
||||
"react-flot": "^1.3.0",
|
||||
|
|
57
src/App.css
57
src/App.css
|
@ -28,3 +28,60 @@ body {
|
|||
.data-table > tbody > tr > td {
|
||||
padding: 6px 16px 6px 16px;
|
||||
}
|
||||
|
||||
.autosuggest-dropdown {
|
||||
position: absolute;
|
||||
border: 1px solid #ced4da;
|
||||
border-radius: .25rem;
|
||||
background-color: #fff;
|
||||
color: #495057;
|
||||
font-size: 1rem;
|
||||
z-index: 1000;
|
||||
min-width: 10rem;
|
||||
top: 100%;
|
||||
float: left;
|
||||
padding: .5rem 1px .5rem 1px;
|
||||
margin: .125rem 0 0 0;
|
||||
top: 40px;
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
.autosuggest-dropdown li {
|
||||
width: 100%;
|
||||
padding: .25rem 1.5rem;
|
||||
clear: both;
|
||||
white-space: nowrap;
|
||||
background-color: transparent;
|
||||
border: 0;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.graph-legend {
|
||||
margin: 15px 0 15px 25px;
|
||||
}
|
||||
|
||||
.graph .flot-overlay {
|
||||
cursor: crosshair;
|
||||
}
|
||||
|
||||
.graph-tooltip {
|
||||
background: rgba(0,0,0,.8);
|
||||
color: #fff;
|
||||
font-family: Arial, Helvetica, sans-serif;
|
||||
font-size: 12px;
|
||||
white-space: nowrap;
|
||||
padding: 8px;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.graph-tooltip .labels {
|
||||
font-size: 11px;
|
||||
line-height: 11px;
|
||||
}
|
||||
|
||||
.graph-tooltip .detail-swatch {
|
||||
display: inline-block;
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
margin: 0 5px 0 0;
|
||||
}
|
||||
|
|
194
src/App.js
194
src/App.js
|
@ -17,7 +17,10 @@ import {
|
|||
} from 'reactstrap';
|
||||
import ReactFlot from 'react-flot';
|
||||
import '../node_modules/react-flot/flot/jquery.flot.time.min';
|
||||
import '../node_modules/react-flot/flot/jquery.flot.crosshair.min';
|
||||
import '../node_modules/react-flot/flot/jquery.flot.tooltip.min';
|
||||
import './App.css';
|
||||
import Downshift from 'downshift';
|
||||
|
||||
class App extends Component {
|
||||
render() {
|
||||
|
@ -55,9 +58,7 @@ class PanelList extends Component {
|
|||
}
|
||||
})
|
||||
.then(json =>
|
||||
this.setState({
|
||||
metrics: json.data,
|
||||
})
|
||||
this.setState({ metrics: json.data })
|
||||
)
|
||||
.catch(error => {
|
||||
this.setState({error})
|
||||
|
@ -100,7 +101,7 @@ class Panel extends Component {
|
|||
|
||||
this.state = {
|
||||
expr: 'rate(node_cpu_seconds_total[1m])',
|
||||
type: 'table', // TODO enum?
|
||||
type: 'graph', // TODO enum?
|
||||
range: 3600,
|
||||
endTime: null,
|
||||
step: null,
|
||||
|
@ -161,7 +162,6 @@ class Panel extends Component {
|
|||
if (resp.ok) {
|
||||
return resp.json();
|
||||
} else {
|
||||
console.log(resp);
|
||||
throw new Error('Unexpected response status: ' + resp.statusText);
|
||||
}
|
||||
})
|
||||
|
@ -180,8 +180,9 @@ class Panel extends Component {
|
|||
});
|
||||
}
|
||||
|
||||
handleExpressionChange(event) {
|
||||
this.setState({expr: event.target.value});
|
||||
handleExpressionChange(expr) {
|
||||
//this.setState({expr: event.target.value});
|
||||
this.setState({expr: expr});
|
||||
}
|
||||
|
||||
render() {
|
||||
|
@ -189,7 +190,12 @@ class Panel extends Component {
|
|||
<>
|
||||
<Row>
|
||||
<Col>
|
||||
<ExpressionInput value={this.state.expr} onChange={this.handleExpressionChange} execute={this.execute}/>
|
||||
<ExpressionInput
|
||||
value={this.state.expr}
|
||||
onChange={this.handleExpressionChange}
|
||||
execute={this.execute}
|
||||
metrics={this.props.metrics}
|
||||
/>
|
||||
{/*<Input type="select" name="selectMetric">
|
||||
{this.props.metrics.map(m => <option key={m}>{m}</option>)}
|
||||
</Input>*/}
|
||||
|
@ -275,21 +281,97 @@ class ExpressionInput extends Component {
|
|||
return this.props.value.split(/\r\n|\r|\n/).length;
|
||||
}
|
||||
|
||||
stateReducer = (state, changes) => {
|
||||
switch (changes.type) {
|
||||
case Downshift.stateChangeTypes.keyDownEnter:
|
||||
case Downshift.stateChangeTypes.clickItem:
|
||||
case Downshift.stateChangeTypes.changeInput:
|
||||
return {
|
||||
...changes,
|
||||
selectedItem: changes.inputValue,
|
||||
};
|
||||
default:
|
||||
return changes;
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<InputGroup className="expression-input">
|
||||
<Input
|
||||
autoFocus
|
||||
type="textarea"
|
||||
rows={this.numRows()}
|
||||
value={this.props.value}
|
||||
onChange={this.props.onChange}
|
||||
onKeyPress={this.handleKeyPress}
|
||||
placeholder="Expression (press Shift+Enter for newlines)" />
|
||||
<InputGroupAddon addonType="append">
|
||||
<Button color="primary" onClick={this.props.execute}>Execute</Button>
|
||||
</InputGroupAddon>
|
||||
</InputGroup>
|
||||
<Downshift
|
||||
inputValue={this.props.value}
|
||||
onInputValueChange={this.props.onChange}
|
||||
selectedItem={this.props.value}
|
||||
>
|
||||
{downshift => (
|
||||
<div>
|
||||
<InputGroup className="expression-input">
|
||||
|
||||
<Input
|
||||
autoFocus
|
||||
type="textarea"
|
||||
rows={this.numRows()}
|
||||
onKeyPress={this.handleKeyPress}
|
||||
placeholder="Expression (press Shift+Enter for newlines)"
|
||||
//onChange={selection => alert(`You selected ${selection}`)}
|
||||
{...downshift.getInputProps({
|
||||
onKeyDown: event => {
|
||||
switch (event.key) {
|
||||
case 'Home':
|
||||
case 'End':
|
||||
// We want to be able to jump to the beginning/end of the input field.
|
||||
// By default, Downshift otherwise jumps to the first/last suggestion item instead.
|
||||
event.nativeEvent.preventDownshiftDefault = true;
|
||||
break;
|
||||
case 'Enter':
|
||||
downshift.closeMenu();
|
||||
break;
|
||||
default:
|
||||
}
|
||||
}
|
||||
})}
|
||||
/>
|
||||
<InputGroupAddon addonType="append">
|
||||
<Button color="primary" onClick={this.props.execute}>Execute</Button>
|
||||
</InputGroupAddon>
|
||||
</InputGroup>
|
||||
{downshift.isOpen &&
|
||||
<ul className="autosuggest-dropdown" {...downshift.getMenuProps()}>
|
||||
{
|
||||
this.props.metrics
|
||||
.filter(item => !downshift.inputValue || item.includes(downshift.inputValue))
|
||||
.slice(0, 100) // Limit DOM rendering to 100 results, as DOM rendering is sloooow.
|
||||
.map((item, index) => (
|
||||
<li
|
||||
{...downshift.getItemProps({
|
||||
key: item,
|
||||
index,
|
||||
item,
|
||||
style: {
|
||||
backgroundColor:
|
||||
downshift.highlightedIndex === index ? 'lightgray' : 'white',
|
||||
fontWeight: downshift.selectedItem === item ? 'bold' : 'normal',
|
||||
},
|
||||
})}
|
||||
>
|
||||
{item}
|
||||
</li>
|
||||
))
|
||||
}
|
||||
</ul>
|
||||
}
|
||||
</div>
|
||||
)}
|
||||
</Downshift>
|
||||
|
||||
// <Input
|
||||
// autoFocus
|
||||
// type="textarea"
|
||||
// rows={this.numRows()}
|
||||
// value={this.props.value}
|
||||
// onChange={this.props.onChange}
|
||||
// onKeyPress={this.handleKeyPress}
|
||||
// placeholder="Expression (press Shift+Enter for newlines)" />
|
||||
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -347,23 +429,74 @@ function getGraphID() {
|
|||
}
|
||||
|
||||
class Graph extends Component {
|
||||
componentDidMount() {
|
||||
this.chart = null;
|
||||
escapeHTML(string) {
|
||||
var entityMap = {
|
||||
"&": "&",
|
||||
"<": "<",
|
||||
">": ">",
|
||||
'"': '"',
|
||||
"'": ''',
|
||||
"/": '/'
|
||||
};
|
||||
|
||||
return String(string).replace(/[&<>"'/]/g, function (s) {
|
||||
return entityMap[s];
|
||||
});
|
||||
}
|
||||
|
||||
renderLabels(labels) {
|
||||
let labelStrings = [];
|
||||
for (let label in labels) {
|
||||
if (label !== '__name__') {
|
||||
labelStrings.push('<strong>' + label + '</strong>: ' + this.escapeHTML(labels[label]));
|
||||
}
|
||||
}
|
||||
return labels = '<div class="labels">' + labelStrings.join('<br>') + '</div>';
|
||||
};
|
||||
|
||||
getOptions() {
|
||||
return {
|
||||
grid: {
|
||||
hoverable: true,
|
||||
clickable: true,
|
||||
autoHighlight: true,
|
||||
mouseActiveRadius: 100,
|
||||
},
|
||||
legend: {
|
||||
container: this.legend,
|
||||
labelFormatter: (s) => {return ' ' + s}
|
||||
},
|
||||
xaxis: {
|
||||
mode: "time",
|
||||
//timeformat: "%Y/%m/%d",
|
||||
mode: 'time',
|
||||
showTicks: true,
|
||||
showMinorTicks: true,
|
||||
// min: (new Date()).getTime(),
|
||||
// max: (new Date(2000, 1, 1)).getTime(),
|
||||
},
|
||||
crosshair: {
|
||||
mode: 'xy',
|
||||
color: '#bbb',
|
||||
},
|
||||
tooltip: {
|
||||
show: true,
|
||||
cssClass: 'graph-tooltip',
|
||||
content: (label, xval, yval, flotItem) => {
|
||||
const series = flotItem.series;
|
||||
var date = '<span class="date">' + new Date(xval).toUTCString() + '</span>';
|
||||
var swatch = '<span class="detail-swatch" style="background-color: ' + series.color + '"></span>';
|
||||
var content = swatch + (series.labels.__name__ || 'value') + ": <strong>" + yval + '</strong>';
|
||||
return date + '<br>' + content + '<br>' + this.renderLabels(series.labels);
|
||||
},
|
||||
defaultTheme: false,
|
||||
lines: true,
|
||||
},
|
||||
series: {
|
||||
lines: {
|
||||
lineWidth: 2,
|
||||
steps: false,
|
||||
},
|
||||
shadowSize: 0,
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -376,6 +509,7 @@ class Graph extends Component {
|
|||
return this.props.data.result.map(ts => {
|
||||
return {
|
||||
label: metricToSeriesName(ts.metric),
|
||||
labels: ts.metric,
|
||||
data: ts.values.map(v => [v[0] * 1000, this.parseValue(v[1])]),
|
||||
};
|
||||
})
|
||||
|
@ -396,15 +530,15 @@ class Graph extends Component {
|
|||
return null;
|
||||
}
|
||||
return (
|
||||
<div>
|
||||
<div className="graph">
|
||||
<ReactFlot
|
||||
id={getGraphID().toString()}
|
||||
data={this.getData()}
|
||||
options={this.getOptions()}
|
||||
width="1900px"
|
||||
height="500px"
|
||||
width="100%"
|
||||
/>
|
||||
<div ref={ref => { this.legend = ref; }}></div>
|
||||
<div className="graph-legend" ref={ref => { this.legend = ref; }}>df</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -415,10 +549,10 @@ function metricToSeriesName(labels) {
|
|||
var labelStrings = [];
|
||||
for (var label in labels) {
|
||||
if (label !== "__name__") {
|
||||
labelStrings.push(label + "=\"" + labels[label] + "\"");
|
||||
labelStrings.push('<b>' + label + "</b>=\"" + labels[label] + "\"");
|
||||
}
|
||||
}
|
||||
tsName += labelStrings.join(",") + "}";
|
||||
tsName += labelStrings.join(", ") + "}";
|
||||
return tsName;
|
||||
};
|
||||
|
||||
|
|
Loading…
Reference in a new issue