mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Merge pull request #12371 from marefr/xaxis_thresholds
Time regions support in graph panel
This commit is contained in:
commit
0810aa2e60
511
devenv/dev-dashboards/panel_tests_graph_time_regions.json
Normal file
511
devenv/dev-dashboards/panel_tests_graph_time_regions.json
Normal file
@ -0,0 +1,511 @@
|
|||||||
|
{
|
||||||
|
"annotations": {
|
||||||
|
"list": [
|
||||||
|
{
|
||||||
|
"builtIn": 1,
|
||||||
|
"datasource": "-- Grafana --",
|
||||||
|
"enable": true,
|
||||||
|
"hide": true,
|
||||||
|
"iconColor": "rgba(0, 211, 255, 1)",
|
||||||
|
"name": "Annotations & Alerts",
|
||||||
|
"type": "dashboard"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"editable": true,
|
||||||
|
"gnetId": null,
|
||||||
|
"graphTooltip": 0,
|
||||||
|
"links": [],
|
||||||
|
"panels": [
|
||||||
|
{
|
||||||
|
"aliasColors": {},
|
||||||
|
"bars": false,
|
||||||
|
"dashLength": 10,
|
||||||
|
"dashes": false,
|
||||||
|
"datasource": "gdev-testdata",
|
||||||
|
"fill": 2,
|
||||||
|
"gridPos": {
|
||||||
|
"h": 8,
|
||||||
|
"w": 24,
|
||||||
|
"x": 0,
|
||||||
|
"y": 0
|
||||||
|
},
|
||||||
|
"id": 2,
|
||||||
|
"legend": {
|
||||||
|
"avg": false,
|
||||||
|
"current": false,
|
||||||
|
"max": false,
|
||||||
|
"min": false,
|
||||||
|
"show": true,
|
||||||
|
"total": false,
|
||||||
|
"values": false
|
||||||
|
},
|
||||||
|
"lines": true,
|
||||||
|
"linewidth": 2,
|
||||||
|
"links": [],
|
||||||
|
"nullPointMode": "null",
|
||||||
|
"percentage": false,
|
||||||
|
"pointradius": 5,
|
||||||
|
"points": false,
|
||||||
|
"renderer": "flot",
|
||||||
|
"seriesOverrides": [],
|
||||||
|
"spaceLength": 10,
|
||||||
|
"stack": false,
|
||||||
|
"steppedLine": false,
|
||||||
|
"targets": [
|
||||||
|
{
|
||||||
|
"refId": "A",
|
||||||
|
"scenarioId": "random_walk",
|
||||||
|
"target": ""
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"thresholds": [],
|
||||||
|
"timeFrom": null,
|
||||||
|
"timeRegions": [
|
||||||
|
{
|
||||||
|
"colorMode": "gray",
|
||||||
|
"fill": true,
|
||||||
|
"fillColor": "rgba(255, 255, 255, 0.03)",
|
||||||
|
"from": "08:30",
|
||||||
|
"fromDayOfWeek": 1,
|
||||||
|
"line": false,
|
||||||
|
"lineColor": "rgba(255, 255, 255, 0.2)",
|
||||||
|
"op": "time",
|
||||||
|
"to": "16:45",
|
||||||
|
"toDayOfWeek": 5
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"timeShift": null,
|
||||||
|
"title": "Business Hours",
|
||||||
|
"tooltip": {
|
||||||
|
"shared": true,
|
||||||
|
"sort": 0,
|
||||||
|
"value_type": "individual"
|
||||||
|
},
|
||||||
|
"type": "graph",
|
||||||
|
"xaxis": {
|
||||||
|
"buckets": null,
|
||||||
|
"mode": "time",
|
||||||
|
"name": null,
|
||||||
|
"show": true,
|
||||||
|
"values": []
|
||||||
|
},
|
||||||
|
"yaxes": [
|
||||||
|
{
|
||||||
|
"format": "short",
|
||||||
|
"label": null,
|
||||||
|
"logBase": 1,
|
||||||
|
"max": null,
|
||||||
|
"min": null,
|
||||||
|
"show": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"format": "short",
|
||||||
|
"label": null,
|
||||||
|
"logBase": 1,
|
||||||
|
"max": null,
|
||||||
|
"min": null,
|
||||||
|
"show": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"yaxis": {
|
||||||
|
"align": false,
|
||||||
|
"alignLevel": null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"aliasColors": {},
|
||||||
|
"bars": false,
|
||||||
|
"dashLength": 10,
|
||||||
|
"dashes": false,
|
||||||
|
"datasource": "gdev-testdata",
|
||||||
|
"fill": 2,
|
||||||
|
"gridPos": {
|
||||||
|
"h": 8,
|
||||||
|
"w": 24,
|
||||||
|
"x": 0,
|
||||||
|
"y": 8
|
||||||
|
},
|
||||||
|
"id": 4,
|
||||||
|
"legend": {
|
||||||
|
"avg": false,
|
||||||
|
"current": false,
|
||||||
|
"max": false,
|
||||||
|
"min": false,
|
||||||
|
"show": true,
|
||||||
|
"total": false,
|
||||||
|
"values": false
|
||||||
|
},
|
||||||
|
"lines": true,
|
||||||
|
"linewidth": 2,
|
||||||
|
"links": [],
|
||||||
|
"nullPointMode": "null",
|
||||||
|
"percentage": false,
|
||||||
|
"pointradius": 5,
|
||||||
|
"points": false,
|
||||||
|
"renderer": "flot",
|
||||||
|
"seriesOverrides": [],
|
||||||
|
"spaceLength": 10,
|
||||||
|
"stack": false,
|
||||||
|
"steppedLine": false,
|
||||||
|
"targets": [
|
||||||
|
{
|
||||||
|
"expr": "",
|
||||||
|
"format": "time_series",
|
||||||
|
"intervalFactor": 1,
|
||||||
|
"refId": "A",
|
||||||
|
"scenarioId": "random_walk",
|
||||||
|
"target": ""
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"thresholds": [],
|
||||||
|
"timeFrom": null,
|
||||||
|
"timeRegions": [
|
||||||
|
{
|
||||||
|
"colorMode": "red",
|
||||||
|
"fill": true,
|
||||||
|
"fillColor": "rgba(255, 255, 255, 0.03)",
|
||||||
|
"from": "20:00",
|
||||||
|
"fromDayOfWeek": 7,
|
||||||
|
"line": false,
|
||||||
|
"lineColor": "rgba(255, 255, 255, 0.2)",
|
||||||
|
"op": "time",
|
||||||
|
"to": "23:00",
|
||||||
|
"toDayOfWeek": 7
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"timeShift": null,
|
||||||
|
"title": "Sunday's 20-23",
|
||||||
|
"tooltip": {
|
||||||
|
"shared": true,
|
||||||
|
"sort": 0,
|
||||||
|
"value_type": "individual"
|
||||||
|
},
|
||||||
|
"type": "graph",
|
||||||
|
"xaxis": {
|
||||||
|
"buckets": null,
|
||||||
|
"mode": "time",
|
||||||
|
"name": null,
|
||||||
|
"show": true,
|
||||||
|
"values": []
|
||||||
|
},
|
||||||
|
"yaxes": [
|
||||||
|
{
|
||||||
|
"format": "short",
|
||||||
|
"label": null,
|
||||||
|
"logBase": 1,
|
||||||
|
"max": null,
|
||||||
|
"min": null,
|
||||||
|
"show": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"format": "short",
|
||||||
|
"label": null,
|
||||||
|
"logBase": 1,
|
||||||
|
"max": null,
|
||||||
|
"min": null,
|
||||||
|
"show": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"yaxis": {
|
||||||
|
"align": false,
|
||||||
|
"alignLevel": null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"aliasColors": {
|
||||||
|
"A-series": "#d683ce"
|
||||||
|
},
|
||||||
|
"bars": false,
|
||||||
|
"dashLength": 10,
|
||||||
|
"dashes": false,
|
||||||
|
"datasource": "gdev-testdata",
|
||||||
|
"fill": 2,
|
||||||
|
"gridPos": {
|
||||||
|
"h": 8,
|
||||||
|
"w": 24,
|
||||||
|
"x": 0,
|
||||||
|
"y": 16
|
||||||
|
},
|
||||||
|
"id": 3,
|
||||||
|
"legend": {
|
||||||
|
"avg": false,
|
||||||
|
"current": false,
|
||||||
|
"max": false,
|
||||||
|
"min": false,
|
||||||
|
"show": true,
|
||||||
|
"total": false,
|
||||||
|
"values": false
|
||||||
|
},
|
||||||
|
"lines": true,
|
||||||
|
"linewidth": 2,
|
||||||
|
"links": [],
|
||||||
|
"nullPointMode": "null",
|
||||||
|
"percentage": false,
|
||||||
|
"pointradius": 0.5,
|
||||||
|
"points": false,
|
||||||
|
"renderer": "flot",
|
||||||
|
"seriesOverrides": [],
|
||||||
|
"spaceLength": 10,
|
||||||
|
"stack": false,
|
||||||
|
"steppedLine": false,
|
||||||
|
"targets": [
|
||||||
|
{
|
||||||
|
"refId": "A",
|
||||||
|
"scenarioId": "random_walk",
|
||||||
|
"target": ""
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"thresholds": [],
|
||||||
|
"timeFrom": null,
|
||||||
|
"timeRegions": [
|
||||||
|
{
|
||||||
|
"colorMode": "custom",
|
||||||
|
"fill": true,
|
||||||
|
"fillColor": "rgba(255, 0, 0, 0.22)",
|
||||||
|
"from": "",
|
||||||
|
"fromDayOfWeek": 1,
|
||||||
|
"line": true,
|
||||||
|
"lineColor": "rgba(255, 0, 0, 0.32)",
|
||||||
|
"op": "time",
|
||||||
|
"to": "",
|
||||||
|
"toDayOfWeek": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"colorMode": "custom",
|
||||||
|
"fill": true,
|
||||||
|
"fillColor": "rgba(255, 127, 0, 0.22)",
|
||||||
|
"fromDayOfWeek": 2,
|
||||||
|
"line": true,
|
||||||
|
"lineColor": "rgba(255, 127, 0, 0.32)",
|
||||||
|
"op": "time",
|
||||||
|
"toDayOfWeek": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"colorMode": "custom",
|
||||||
|
"fill": true,
|
||||||
|
"fillColor": "rgba(255, 255, 0, 0.22)",
|
||||||
|
"fromDayOfWeek": 3,
|
||||||
|
"line": true,
|
||||||
|
"lineColor": "rgba(255, 255, 0, 0.22)",
|
||||||
|
"op": "time",
|
||||||
|
"toDayOfWeek": 3
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"colorMode": "custom",
|
||||||
|
"fill": true,
|
||||||
|
"fillColor": "rgba(0, 255, 0, 0.22)",
|
||||||
|
"fromDayOfWeek": 4,
|
||||||
|
"line": true,
|
||||||
|
"lineColor": "rgba(0, 255, 0, 0.32)",
|
||||||
|
"op": "time",
|
||||||
|
"toDayOfWeek": 4
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"colorMode": "custom",
|
||||||
|
"fill": true,
|
||||||
|
"fillColor": "rgba(0, 0, 255, 0.22)",
|
||||||
|
"fromDayOfWeek": 5,
|
||||||
|
"line": true,
|
||||||
|
"lineColor": "rgba(0, 0, 255, 0.32)",
|
||||||
|
"op": "time",
|
||||||
|
"toDayOfWeek": 5
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"colorMode": "custom",
|
||||||
|
"fill": true,
|
||||||
|
"fillColor": "rgba(75, 0, 130, 0.22)",
|
||||||
|
"fromDayOfWeek": 6,
|
||||||
|
"line": true,
|
||||||
|
"lineColor": "rgba(75, 0, 130, 0.32)",
|
||||||
|
"op": "time",
|
||||||
|
"toDayOfWeek": 6
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"colorMode": "custom",
|
||||||
|
"fill": true,
|
||||||
|
"fillColor": "rgba(148, 0, 211, 0.22)",
|
||||||
|
"fromDayOfWeek": 7,
|
||||||
|
"line": true,
|
||||||
|
"lineColor": "rgba(148, 0, 211, 0.32)",
|
||||||
|
"op": "time",
|
||||||
|
"toDayOfWeek": 7
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"timeShift": null,
|
||||||
|
"title": "Each day of week",
|
||||||
|
"tooltip": {
|
||||||
|
"shared": true,
|
||||||
|
"sort": 0,
|
||||||
|
"value_type": "individual"
|
||||||
|
},
|
||||||
|
"type": "graph",
|
||||||
|
"xaxis": {
|
||||||
|
"buckets": null,
|
||||||
|
"mode": "time",
|
||||||
|
"name": null,
|
||||||
|
"show": true,
|
||||||
|
"values": []
|
||||||
|
},
|
||||||
|
"yaxes": [
|
||||||
|
{
|
||||||
|
"format": "short",
|
||||||
|
"label": null,
|
||||||
|
"logBase": 1,
|
||||||
|
"max": null,
|
||||||
|
"min": null,
|
||||||
|
"show": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"format": "short",
|
||||||
|
"label": null,
|
||||||
|
"logBase": 1,
|
||||||
|
"max": null,
|
||||||
|
"min": null,
|
||||||
|
"show": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"yaxis": {
|
||||||
|
"align": false,
|
||||||
|
"alignLevel": null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"aliasColors": {},
|
||||||
|
"bars": false,
|
||||||
|
"dashLength": 10,
|
||||||
|
"dashes": false,
|
||||||
|
"datasource": "gdev-testdata",
|
||||||
|
"fill": 2,
|
||||||
|
"gridPos": {
|
||||||
|
"h": 8,
|
||||||
|
"w": 24,
|
||||||
|
"x": 0,
|
||||||
|
"y": 24
|
||||||
|
},
|
||||||
|
"id": 5,
|
||||||
|
"legend": {
|
||||||
|
"avg": false,
|
||||||
|
"current": false,
|
||||||
|
"max": false,
|
||||||
|
"min": false,
|
||||||
|
"show": true,
|
||||||
|
"total": false,
|
||||||
|
"values": false
|
||||||
|
},
|
||||||
|
"lines": true,
|
||||||
|
"linewidth": 2,
|
||||||
|
"links": [],
|
||||||
|
"nullPointMode": "null",
|
||||||
|
"percentage": false,
|
||||||
|
"pointradius": 5,
|
||||||
|
"points": false,
|
||||||
|
"renderer": "flot",
|
||||||
|
"seriesOverrides": [],
|
||||||
|
"spaceLength": 10,
|
||||||
|
"stack": false,
|
||||||
|
"steppedLine": false,
|
||||||
|
"targets": [
|
||||||
|
{
|
||||||
|
"expr": "",
|
||||||
|
"format": "time_series",
|
||||||
|
"intervalFactor": 1,
|
||||||
|
"refId": "A",
|
||||||
|
"scenarioId": "random_walk",
|
||||||
|
"target": ""
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"thresholds": [],
|
||||||
|
"timeFrom": null,
|
||||||
|
"timeRegions": [
|
||||||
|
{
|
||||||
|
"colorMode": "red",
|
||||||
|
"fill": false,
|
||||||
|
"from": "05:00",
|
||||||
|
"line": true,
|
||||||
|
"op": "time"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"timeShift": null,
|
||||||
|
"title": "05:00",
|
||||||
|
"tooltip": {
|
||||||
|
"shared": true,
|
||||||
|
"sort": 0,
|
||||||
|
"value_type": "individual"
|
||||||
|
},
|
||||||
|
"type": "graph",
|
||||||
|
"xaxis": {
|
||||||
|
"buckets": null,
|
||||||
|
"mode": "time",
|
||||||
|
"name": null,
|
||||||
|
"show": true,
|
||||||
|
"values": []
|
||||||
|
},
|
||||||
|
"yaxes": [
|
||||||
|
{
|
||||||
|
"format": "short",
|
||||||
|
"label": null,
|
||||||
|
"logBase": 1,
|
||||||
|
"max": null,
|
||||||
|
"min": null,
|
||||||
|
"show": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"format": "short",
|
||||||
|
"label": null,
|
||||||
|
"logBase": 1,
|
||||||
|
"max": null,
|
||||||
|
"min": null,
|
||||||
|
"show": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"yaxis": {
|
||||||
|
"align": false,
|
||||||
|
"alignLevel": null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"refresh": false,
|
||||||
|
"schemaVersion": 16,
|
||||||
|
"style": "dark",
|
||||||
|
"tags": [
|
||||||
|
"gdev",
|
||||||
|
"panel-tests"
|
||||||
|
],
|
||||||
|
"templating": {
|
||||||
|
"list": []
|
||||||
|
},
|
||||||
|
"time": {
|
||||||
|
"from": "now-30d",
|
||||||
|
"to": "now"
|
||||||
|
},
|
||||||
|
"timepicker": {
|
||||||
|
"refresh_intervals": [
|
||||||
|
"5s",
|
||||||
|
"10s",
|
||||||
|
"30s",
|
||||||
|
"1m",
|
||||||
|
"5m",
|
||||||
|
"15m",
|
||||||
|
"30m",
|
||||||
|
"1h",
|
||||||
|
"2h",
|
||||||
|
"1d"
|
||||||
|
],
|
||||||
|
"time_options": [
|
||||||
|
"5m",
|
||||||
|
"15m",
|
||||||
|
"1h",
|
||||||
|
"6h",
|
||||||
|
"12h",
|
||||||
|
"24h",
|
||||||
|
"2d",
|
||||||
|
"7d",
|
||||||
|
"30d"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"timezone": "browser",
|
||||||
|
"title": "Panel Tests - Graph (Time Regions)",
|
||||||
|
"uid": "XMjIZPmik",
|
||||||
|
"version": 1
|
||||||
|
}
|
@ -186,6 +186,14 @@ There is an option under Series overrides to draw lines as dashes. Set Dashes to
|
|||||||
Thresholds allow you to add arbitrary lines or sections to the graph to make it easier to see when
|
Thresholds allow you to add arbitrary lines or sections to the graph to make it easier to see when
|
||||||
the graph crosses a particular threshold.
|
the graph crosses a particular threshold.
|
||||||
|
|
||||||
|
### Time Regions
|
||||||
|
|
||||||
|
> Only available in Grafana v5.4 and above.
|
||||||
|
|
||||||
|
{{< docs-imagebox img="/img/docs/v54/graph_time_regions.png" max-width= "800px" >}}
|
||||||
|
|
||||||
|
Time regions allow you to highlight certain time regions of the graph to make it easier to see for example weekends, business hours and/or off work hours.
|
||||||
|
|
||||||
## Time Range
|
## Time Range
|
||||||
|
|
||||||
{{< docs-imagebox img="/img/docs/v51/graph-time-range.png" max-width= "900px" >}}
|
{{< docs-imagebox img="/img/docs/v51/graph-time-range.png" max-width= "900px" >}}
|
||||||
|
@ -16,6 +16,7 @@ import { tickStep } from 'app/core/utils/ticks';
|
|||||||
import { appEvents, coreModule, updateLegendValues } from 'app/core/core';
|
import { appEvents, coreModule, updateLegendValues } from 'app/core/core';
|
||||||
import GraphTooltip from './graph_tooltip';
|
import GraphTooltip from './graph_tooltip';
|
||||||
import { ThresholdManager } from './threshold_manager';
|
import { ThresholdManager } from './threshold_manager';
|
||||||
|
import { TimeRegionManager } from './time_region_manager';
|
||||||
import { EventManager } from 'app/features/annotations/all';
|
import { EventManager } from 'app/features/annotations/all';
|
||||||
import { convertToHistogramData } from './histogram';
|
import { convertToHistogramData } from './histogram';
|
||||||
import { alignYLevel } from './align_yaxes';
|
import { alignYLevel } from './align_yaxes';
|
||||||
@ -38,6 +39,7 @@ class GraphElement {
|
|||||||
panelWidth: number;
|
panelWidth: number;
|
||||||
eventManager: EventManager;
|
eventManager: EventManager;
|
||||||
thresholdManager: ThresholdManager;
|
thresholdManager: ThresholdManager;
|
||||||
|
timeRegionManager: TimeRegionManager;
|
||||||
legendElem: HTMLElement;
|
legendElem: HTMLElement;
|
||||||
|
|
||||||
constructor(private scope, private elem, private timeSrv) {
|
constructor(private scope, private elem, private timeSrv) {
|
||||||
@ -49,6 +51,7 @@ class GraphElement {
|
|||||||
this.panelWidth = 0;
|
this.panelWidth = 0;
|
||||||
this.eventManager = new EventManager(this.ctrl);
|
this.eventManager = new EventManager(this.ctrl);
|
||||||
this.thresholdManager = new ThresholdManager(this.ctrl);
|
this.thresholdManager = new ThresholdManager(this.ctrl);
|
||||||
|
this.timeRegionManager = new TimeRegionManager(this.ctrl);
|
||||||
this.tooltip = new GraphTooltip(this.elem, this.ctrl.dashboard, this.scope, () => {
|
this.tooltip = new GraphTooltip(this.elem, this.ctrl.dashboard, this.scope, () => {
|
||||||
return this.sortedSeries;
|
return this.sortedSeries;
|
||||||
});
|
});
|
||||||
@ -125,6 +128,7 @@ class GraphElement {
|
|||||||
|
|
||||||
onPanelTeardown() {
|
onPanelTeardown() {
|
||||||
this.thresholdManager = null;
|
this.thresholdManager = null;
|
||||||
|
this.timeRegionManager = null;
|
||||||
|
|
||||||
if (this.plot) {
|
if (this.plot) {
|
||||||
this.plot.destroy();
|
this.plot.destroy();
|
||||||
@ -215,6 +219,7 @@ class GraphElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.thresholdManager.draw(plot);
|
this.thresholdManager.draw(plot);
|
||||||
|
this.timeRegionManager.draw(plot);
|
||||||
}
|
}
|
||||||
|
|
||||||
processOffsetHook(plot, gridMargin) {
|
processOffsetHook(plot, gridMargin) {
|
||||||
@ -293,6 +298,7 @@ class GraphElement {
|
|||||||
this.prepareXAxis(options, this.panel);
|
this.prepareXAxis(options, this.panel);
|
||||||
this.configureYAxisOptions(this.data, options);
|
this.configureYAxisOptions(this.data, options);
|
||||||
this.thresholdManager.addFlotOptions(options, this.panel);
|
this.thresholdManager.addFlotOptions(options, this.panel);
|
||||||
|
this.timeRegionManager.addFlotOptions(options, this.panel);
|
||||||
this.eventManager.addFlotEvents(this.annotations, options);
|
this.eventManager.addFlotEvents(this.annotations, options);
|
||||||
|
|
||||||
this.sortedSeries = this.sortSeries(this.data, this.panel);
|
this.sortedSeries = this.sortSeries(this.data, this.panel);
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import './graph';
|
import './graph';
|
||||||
import './series_overrides_ctrl';
|
import './series_overrides_ctrl';
|
||||||
import './thresholds_form';
|
import './thresholds_form';
|
||||||
|
import './time_regions_form';
|
||||||
|
|
||||||
import template from './template';
|
import template from './template';
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
@ -111,6 +112,7 @@ class GraphCtrl extends MetricsPanelCtrl {
|
|||||||
// other style overrides
|
// other style overrides
|
||||||
seriesOverrides: [],
|
seriesOverrides: [],
|
||||||
thresholds: [],
|
thresholds: [],
|
||||||
|
timeRegions: [],
|
||||||
};
|
};
|
||||||
|
|
||||||
/** @ngInject */
|
/** @ngInject */
|
||||||
|
262
public/app/plugins/panel/graph/specs/time_region_manager.test.ts
Normal file
262
public/app/plugins/panel/graph/specs/time_region_manager.test.ts
Normal file
@ -0,0 +1,262 @@
|
|||||||
|
import { TimeRegionManager, colorModes } from '../time_region_manager';
|
||||||
|
import moment from 'moment';
|
||||||
|
|
||||||
|
describe('TimeRegionManager', () => {
|
||||||
|
function plotOptionsScenario(desc, func) {
|
||||||
|
describe(desc, () => {
|
||||||
|
const ctx: any = {
|
||||||
|
panel: {
|
||||||
|
timeRegions: [],
|
||||||
|
},
|
||||||
|
options: {
|
||||||
|
grid: { markings: [] },
|
||||||
|
},
|
||||||
|
panelCtrl: {
|
||||||
|
range: {},
|
||||||
|
dashboard: {
|
||||||
|
isTimezoneUtc: () => false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
ctx.setup = (regions, from, to) => {
|
||||||
|
ctx.panel.timeRegions = regions;
|
||||||
|
ctx.panelCtrl.range.from = from;
|
||||||
|
ctx.panelCtrl.range.to = to;
|
||||||
|
const manager = new TimeRegionManager(ctx.panelCtrl);
|
||||||
|
manager.addFlotOptions(ctx.options, ctx.panel);
|
||||||
|
};
|
||||||
|
|
||||||
|
ctx.printScenario = () => {
|
||||||
|
console.log(
|
||||||
|
`Time range: from=${ctx.panelCtrl.range.from.format()}, to=${ctx.panelCtrl.range.to.format()}`,
|
||||||
|
ctx.panelCtrl.range.from._isUTC
|
||||||
|
);
|
||||||
|
ctx.options.grid.markings.forEach((m, i) => {
|
||||||
|
console.log(
|
||||||
|
`Marking (${i}): from=${moment(m.xaxis.from).format()}, to=${moment(m.xaxis.to).format()}, color=${m.color}`
|
||||||
|
);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
func(ctx);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('When creating plot markings using local time', () => {
|
||||||
|
plotOptionsScenario('for day of week region', ctx => {
|
||||||
|
const regions = [{ fromDayOfWeek: 1, toDayOfWeek: 1, fill: true, line: true, colorMode: 'red' }];
|
||||||
|
const from = moment('2018-01-01T00:00:00+01:00');
|
||||||
|
const to = moment('2018-01-01T23:59:00+01:00');
|
||||||
|
ctx.setup(regions, from, to);
|
||||||
|
|
||||||
|
it('should add 3 markings', () => {
|
||||||
|
expect(ctx.options.grid.markings.length).toBe(3);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should add fill', () => {
|
||||||
|
const markings = ctx.options.grid.markings;
|
||||||
|
expect(moment(markings[0].xaxis.from).format()).toBe(moment('2018-01-01T01:00:00+01:00').format());
|
||||||
|
expect(moment(markings[0].xaxis.to).format()).toBe(moment('2018-01-02T00:59:59+01:00').format());
|
||||||
|
expect(markings[0].color).toBe(colorModes.red.color.fill);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should add line before', () => {
|
||||||
|
const markings = ctx.options.grid.markings;
|
||||||
|
expect(moment(markings[1].xaxis.from).format()).toBe(moment('2018-01-01T01:00:00+01:00').format());
|
||||||
|
expect(moment(markings[1].xaxis.to).format()).toBe(moment('2018-01-01T01:00:00+01:00').format());
|
||||||
|
expect(markings[1].color).toBe(colorModes.red.color.line);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should add line after', () => {
|
||||||
|
const markings = ctx.options.grid.markings;
|
||||||
|
expect(moment(markings[2].xaxis.from).format()).toBe(moment('2018-01-02T00:59:59+01:00').format());
|
||||||
|
expect(moment(markings[2].xaxis.to).format()).toBe(moment('2018-01-02T00:59:59+01:00').format());
|
||||||
|
expect(markings[2].color).toBe(colorModes.red.color.line);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
plotOptionsScenario('for time from region', ctx => {
|
||||||
|
const regions = [{ from: '05:00', fill: true, colorMode: 'red' }];
|
||||||
|
const from = moment('2018-01-01T00:00+01:00');
|
||||||
|
const to = moment('2018-01-03T23:59+01:00');
|
||||||
|
ctx.setup(regions, from, to);
|
||||||
|
|
||||||
|
it('should add 3 markings', () => {
|
||||||
|
expect(ctx.options.grid.markings.length).toBe(3);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should add one fill at 05:00 each day', () => {
|
||||||
|
const markings = ctx.options.grid.markings;
|
||||||
|
|
||||||
|
expect(moment(markings[0].xaxis.from).format()).toBe(moment('2018-01-01T06:00:00+01:00').format());
|
||||||
|
expect(moment(markings[0].xaxis.to).format()).toBe(moment('2018-01-01T06:00:00+01:00').format());
|
||||||
|
expect(markings[0].color).toBe(colorModes.red.color.fill);
|
||||||
|
|
||||||
|
expect(moment(markings[1].xaxis.from).format()).toBe(moment('2018-01-02T06:00:00+01:00').format());
|
||||||
|
expect(moment(markings[1].xaxis.to).format()).toBe(moment('2018-01-02T06:00:00+01:00').format());
|
||||||
|
expect(markings[1].color).toBe(colorModes.red.color.fill);
|
||||||
|
|
||||||
|
expect(moment(markings[2].xaxis.from).format()).toBe(moment('2018-01-03T06:00:00+01:00').format());
|
||||||
|
expect(moment(markings[2].xaxis.to).format()).toBe(moment('2018-01-03T06:00:00+01:00').format());
|
||||||
|
expect(markings[2].color).toBe(colorModes.red.color.fill);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
plotOptionsScenario('for time to region', ctx => {
|
||||||
|
const regions = [{ to: '05:00', fill: true, colorMode: 'red' }];
|
||||||
|
const from = moment('2018-02-01T00:00+01:00');
|
||||||
|
const to = moment('2018-02-03T23:59+01:00');
|
||||||
|
ctx.setup(regions, from, to);
|
||||||
|
|
||||||
|
it('should add 3 markings', () => {
|
||||||
|
expect(ctx.options.grid.markings.length).toBe(3);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should add one fill at 05:00 each day', () => {
|
||||||
|
const markings = ctx.options.grid.markings;
|
||||||
|
|
||||||
|
expect(moment(markings[0].xaxis.from).format()).toBe(moment('2018-02-01T06:00:00+01:00').format());
|
||||||
|
expect(moment(markings[0].xaxis.to).format()).toBe(moment('2018-02-01T06:00:00+01:00').format());
|
||||||
|
expect(markings[0].color).toBe(colorModes.red.color.fill);
|
||||||
|
|
||||||
|
expect(moment(markings[1].xaxis.from).format()).toBe(moment('2018-02-02T06:00:00+01:00').format());
|
||||||
|
expect(moment(markings[1].xaxis.to).format()).toBe(moment('2018-02-02T06:00:00+01:00').format());
|
||||||
|
expect(markings[1].color).toBe(colorModes.red.color.fill);
|
||||||
|
|
||||||
|
expect(moment(markings[2].xaxis.from).format()).toBe(moment('2018-02-03T06:00:00+01:00').format());
|
||||||
|
expect(moment(markings[2].xaxis.to).format()).toBe(moment('2018-02-03T06:00:00+01:00').format());
|
||||||
|
expect(markings[2].color).toBe(colorModes.red.color.fill);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
plotOptionsScenario('for day of week from/to region', ctx => {
|
||||||
|
const regions = [{ fromDayOfWeek: 7, toDayOfWeek: 7, fill: true, colorMode: 'red' }];
|
||||||
|
const from = moment('2018-01-01T18:45:05+01:00');
|
||||||
|
const to = moment('2018-01-22T08:27:00+01:00');
|
||||||
|
ctx.setup(regions, from, to);
|
||||||
|
|
||||||
|
it('should add 3 markings', () => {
|
||||||
|
expect(ctx.options.grid.markings.length).toBe(3);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should add one fill at each sunday', () => {
|
||||||
|
const markings = ctx.options.grid.markings;
|
||||||
|
|
||||||
|
expect(moment(markings[0].xaxis.from).format()).toBe(moment('2018-01-07T01:00:00+01:00').format());
|
||||||
|
expect(moment(markings[0].xaxis.to).format()).toBe(moment('2018-01-08T00:59:59+01:00').format());
|
||||||
|
expect(markings[0].color).toBe(colorModes.red.color.fill);
|
||||||
|
|
||||||
|
expect(moment(markings[1].xaxis.from).format()).toBe(moment('2018-01-14T01:00:00+01:00').format());
|
||||||
|
expect(moment(markings[1].xaxis.to).format()).toBe(moment('2018-01-15T00:59:59+01:00').format());
|
||||||
|
expect(markings[1].color).toBe(colorModes.red.color.fill);
|
||||||
|
|
||||||
|
expect(moment(markings[2].xaxis.from).format()).toBe(moment('2018-01-21T01:00:00+01:00').format());
|
||||||
|
expect(moment(markings[2].xaxis.to).format()).toBe(moment('2018-01-22T00:59:59+01:00').format());
|
||||||
|
expect(markings[2].color).toBe(colorModes.red.color.fill);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
plotOptionsScenario('for day of week from region', ctx => {
|
||||||
|
const regions = [{ fromDayOfWeek: 7, fill: true, colorMode: 'red' }];
|
||||||
|
const from = moment('2018-01-01T18:45:05+01:00');
|
||||||
|
const to = moment('2018-01-22T08:27:00+01:00');
|
||||||
|
ctx.setup(regions, from, to);
|
||||||
|
|
||||||
|
it('should add 3 markings', () => {
|
||||||
|
expect(ctx.options.grid.markings.length).toBe(3);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should add one fill at each sunday', () => {
|
||||||
|
const markings = ctx.options.grid.markings;
|
||||||
|
|
||||||
|
expect(moment(markings[0].xaxis.from).format()).toBe(moment('2018-01-07T01:00:00+01:00').format());
|
||||||
|
expect(moment(markings[0].xaxis.to).format()).toBe(moment('2018-01-08T00:59:59+01:00').format());
|
||||||
|
expect(markings[0].color).toBe(colorModes.red.color.fill);
|
||||||
|
|
||||||
|
expect(moment(markings[1].xaxis.from).format()).toBe(moment('2018-01-14T01:00:00+01:00').format());
|
||||||
|
expect(moment(markings[1].xaxis.to).format()).toBe(moment('2018-01-15T00:59:59+01:00').format());
|
||||||
|
expect(markings[1].color).toBe(colorModes.red.color.fill);
|
||||||
|
|
||||||
|
expect(moment(markings[2].xaxis.from).format()).toBe(moment('2018-01-21T01:00:00+01:00').format());
|
||||||
|
expect(moment(markings[2].xaxis.to).format()).toBe(moment('2018-01-22T00:59:59+01:00').format());
|
||||||
|
expect(markings[2].color).toBe(colorModes.red.color.fill);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
plotOptionsScenario('for day of week to region', ctx => {
|
||||||
|
const regions = [{ toDayOfWeek: 7, fill: true, colorMode: 'red' }];
|
||||||
|
const from = moment('2018-01-01T18:45:05+01:00');
|
||||||
|
const to = moment('2018-01-22T08:27:00+01:00');
|
||||||
|
ctx.setup(regions, from, to);
|
||||||
|
|
||||||
|
it('should add 3 markings', () => {
|
||||||
|
expect(ctx.options.grid.markings.length).toBe(3);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should add one fill at each sunday', () => {
|
||||||
|
const markings = ctx.options.grid.markings;
|
||||||
|
|
||||||
|
expect(moment(markings[0].xaxis.from).format()).toBe(moment('2018-01-07T01:00:00+01:00').format());
|
||||||
|
expect(moment(markings[0].xaxis.to).format()).toBe(moment('2018-01-08T00:59:59+01:00').format());
|
||||||
|
expect(markings[0].color).toBe(colorModes.red.color.fill);
|
||||||
|
|
||||||
|
expect(moment(markings[1].xaxis.from).format()).toBe(moment('2018-01-14T01:00:00+01:00').format());
|
||||||
|
expect(moment(markings[1].xaxis.to).format()).toBe(moment('2018-01-15T00:59:59+01:00').format());
|
||||||
|
expect(markings[1].color).toBe(colorModes.red.color.fill);
|
||||||
|
|
||||||
|
expect(moment(markings[2].xaxis.from).format()).toBe(moment('2018-01-21T01:00:00+01:00').format());
|
||||||
|
expect(moment(markings[2].xaxis.to).format()).toBe(moment('2018-01-22T00:59:59+01:00').format());
|
||||||
|
expect(markings[2].color).toBe(colorModes.red.color.fill);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
plotOptionsScenario('for day of week from/to time region with daylight saving time', ctx => {
|
||||||
|
const regions = [{ fromDayOfWeek: 7, from: '20:00', toDayOfWeek: 7, to: '23:00', fill: true, colorMode: 'red' }];
|
||||||
|
const from = moment('2018-03-17T06:00:00+01:00');
|
||||||
|
const to = moment('2018-04-03T06:00:00+02:00');
|
||||||
|
ctx.setup(regions, from, to);
|
||||||
|
|
||||||
|
it('should add 3 markings', () => {
|
||||||
|
expect(ctx.options.grid.markings.length).toBe(3);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should add one fill at each sunday between 20:00 and 23:00', () => {
|
||||||
|
const markings = ctx.options.grid.markings;
|
||||||
|
|
||||||
|
expect(moment(markings[0].xaxis.from).format()).toBe(moment('2018-03-18T21:00:00+01:00').format());
|
||||||
|
expect(moment(markings[0].xaxis.to).format()).toBe(moment('2018-03-19T00:00:00+01:00').format());
|
||||||
|
|
||||||
|
expect(moment(markings[1].xaxis.from).format()).toBe(moment('2018-03-25T22:00:00+02:00').format());
|
||||||
|
expect(moment(markings[1].xaxis.to).format()).toBe(moment('2018-03-26T01:00:00+02:00').format());
|
||||||
|
|
||||||
|
expect(moment(markings[2].xaxis.from).format()).toBe(moment('2018-04-01T22:00:00+02:00').format());
|
||||||
|
expect(moment(markings[2].xaxis.to).format()).toBe(moment('2018-04-02T01:00:00+02:00').format());
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
plotOptionsScenario('for each day of week with winter time', ctx => {
|
||||||
|
const regions = [{ fromDayOfWeek: 7, toDayOfWeek: 7, fill: true, colorMode: 'red' }];
|
||||||
|
const from = moment('2018-10-20T14:50:11+02:00');
|
||||||
|
const to = moment('2018-11-07T12:56:23+01:00');
|
||||||
|
ctx.setup(regions, from, to);
|
||||||
|
|
||||||
|
it('should add 3 markings', () => {
|
||||||
|
expect(ctx.options.grid.markings.length).toBe(3);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should add one fill at each sunday', () => {
|
||||||
|
const markings = ctx.options.grid.markings;
|
||||||
|
|
||||||
|
expect(moment(markings[0].xaxis.from).format()).toBe(moment('2018-10-21T02:00:00+02:00').format());
|
||||||
|
expect(moment(markings[0].xaxis.to).format()).toBe(moment('2018-10-22T01:59:59+02:00').format());
|
||||||
|
|
||||||
|
expect(moment(markings[1].xaxis.from).format()).toBe(moment('2018-10-28T02:00:00+02:00').format());
|
||||||
|
expect(moment(markings[1].xaxis.to).format()).toBe(moment('2018-10-29T00:59:59+01:00').format());
|
||||||
|
|
||||||
|
expect(moment(markings[2].xaxis.from).format()).toBe(moment('2018-11-04T01:00:00+01:00').format());
|
||||||
|
expect(moment(markings[2].xaxis.to).format()).toBe(moment('2018-11-05T00:59:59+01:00').format());
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
@ -14,6 +14,11 @@
|
|||||||
Thresholds <span class="muted">({{ctrl.panel.thresholds.length}})</span>
|
Thresholds <span class="muted">({{ctrl.panel.thresholds.length}})</span>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
|
<li ng-class="{active: ctrl.subTabIndex === 3}">
|
||||||
|
<a ng-click="ctrl.subTabIndex = 3">
|
||||||
|
Time regions <span class="muted">({{ctrl.panel.timeRegions.length}})</span>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</aside>
|
</aside>
|
||||||
|
|
||||||
@ -132,4 +137,8 @@
|
|||||||
<graph-threshold-form panel-ctrl="ctrl"></graph-threshold-form>
|
<graph-threshold-form panel-ctrl="ctrl"></graph-threshold-form>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="edit-tab-content" ng-if="ctrl.subTabIndex === 3">
|
||||||
|
<graph-time-region-form panel-ctrl="ctrl"></graph-time-region-form>
|
||||||
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
77
public/app/plugins/panel/graph/thresholds_form.html
Normal file
77
public/app/plugins/panel/graph/thresholds_form.html
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
<div class="gf-form-group">
|
||||||
|
<h5>Thresholds</h5>
|
||||||
|
<p class="muted" ng-show="ctrl.disabled">
|
||||||
|
Visual thresholds options <strong>disabled.</strong>
|
||||||
|
Visit the Alert tab update your thresholds. <br>
|
||||||
|
To re-enable thresholds, the alert rule must be deleted from this panel.
|
||||||
|
</p>
|
||||||
|
<div ng-class="{'thresholds-form-disabled': ctrl.disabled}">
|
||||||
|
<div class="gf-form-inline" ng-repeat="threshold in ctrl.panel.thresholds">
|
||||||
|
<div class="gf-form">
|
||||||
|
<label class="gf-form-label">T{{$index+1}}</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="gf-form">
|
||||||
|
<div class="gf-form-select-wrapper">
|
||||||
|
<select class="gf-form-input" ng-model="threshold.op"
|
||||||
|
ng-options="f for f in ['gt', 'lt']" ng-change="ctrl.render()" ng-disabled="ctrl.disabled"></select>
|
||||||
|
</div>
|
||||||
|
<input type="number" ng-model="threshold.value" class="gf-form-input width-8"
|
||||||
|
ng-change="ctrl.render()" placeholder="value" ng-disabled="ctrl.disabled">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="gf-form">
|
||||||
|
<label class="gf-form-label">Color</label>
|
||||||
|
<div class="gf-form-select-wrapper">
|
||||||
|
<select class="gf-form-input" ng-model="threshold.colorMode"
|
||||||
|
ng-options="f for f in ['custom', 'critical', 'warning', 'ok']" ng-change="ctrl.render()" ng-disabled="ctrl.disabled">
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<gf-form-switch class="gf-form" label="Fill" checked="threshold.fill"
|
||||||
|
on-change="ctrl.render()" ng-disabled="ctrl.disabled"></gf-form-switch>
|
||||||
|
|
||||||
|
<div class="gf-form" ng-if="threshold.fill && threshold.colorMode === 'custom'">
|
||||||
|
<label class="gf-form-label">Fill color</label>
|
||||||
|
<span class="gf-form-label">
|
||||||
|
<color-picker color="threshold.fillColor" onChange="ctrl.onFillColorChange($index)"></color-picker>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<gf-form-switch class="gf-form" label="Line" checked="threshold.line"
|
||||||
|
on-change="ctrl.render()" ng-disabled="ctrl.disabled"></gf-form-switch>
|
||||||
|
|
||||||
|
<div class="gf-form" ng-if="threshold.line && threshold.colorMode === 'custom'">
|
||||||
|
<label class="gf-form-label">Line color</label>
|
||||||
|
<span class="gf-form-label">
|
||||||
|
<color-picker color="threshold.lineColor" onChange="ctrl.onLineColorChange($index)"></color-picker>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="gf-form">
|
||||||
|
<label class="gf-form-label">Y-Axis</label>
|
||||||
|
<div class="gf-form-select-wrapper">
|
||||||
|
<select class="gf-form-input" ng-model="threshold.yaxis"
|
||||||
|
ng-init="threshold.yaxis = threshold.yaxis === 'left' || threshold.yaxis === 'right' ? threshold.yaxis : 'left'"
|
||||||
|
ng-options="f for f in ['left', 'right']" ng-change="ctrl.render()" ng-disabled="ctrl.disabled">
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="gf-form">
|
||||||
|
<label class="gf-form-label">
|
||||||
|
<a class="pointer" ng-click="ctrl.removeThreshold($index)" ng-disabled="ctrl.disabled">
|
||||||
|
<i class="fa fa-trash"></i>
|
||||||
|
</a>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="gf-form-button-row">
|
||||||
|
<button class="btn btn-inverse" ng-click="ctrl.addThreshold()" ng-disabled="ctrl.disabled">
|
||||||
|
<i class="fa fa-plus"></i> Add Threshold
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
@ -58,90 +58,10 @@ export class ThresholdFormCtrl {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const template = `
|
|
||||||
<div class="gf-form-group">
|
|
||||||
<h5>Thresholds</h5>
|
|
||||||
<p class="muted" ng-show="ctrl.disabled">
|
|
||||||
Visual thresholds options <strong>disabled.</strong>
|
|
||||||
Visit the Alert tab update your thresholds. <br>
|
|
||||||
To re-enable thresholds, the alert rule must be deleted from this panel.
|
|
||||||
</p>
|
|
||||||
<div ng-class="{'thresholds-form-disabled': ctrl.disabled}">
|
|
||||||
<div class="gf-form-inline" ng-repeat="threshold in ctrl.panel.thresholds">
|
|
||||||
<div class="gf-form">
|
|
||||||
<label class="gf-form-label">T{{$index+1}}</label>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="gf-form">
|
|
||||||
<div class="gf-form-select-wrapper">
|
|
||||||
<select class="gf-form-input" ng-model="threshold.op"
|
|
||||||
ng-options="f for f in ['gt', 'lt']" ng-change="ctrl.render()" ng-disabled="ctrl.disabled"></select>
|
|
||||||
</div>
|
|
||||||
<input type="number" ng-model="threshold.value" class="gf-form-input width-8"
|
|
||||||
ng-change="ctrl.render()" placeholder="value" ng-disabled="ctrl.disabled">
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="gf-form">
|
|
||||||
<label class="gf-form-label">Color</label>
|
|
||||||
<div class="gf-form-select-wrapper">
|
|
||||||
<select class="gf-form-input" ng-model="threshold.colorMode"
|
|
||||||
ng-options="f for f in ['custom', 'critical', 'warning', 'ok']" ng-change="ctrl.render()" ng-disabled="ctrl.disabled">
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<gf-form-switch class="gf-form" label="Fill" checked="threshold.fill"
|
|
||||||
on-change="ctrl.render()" ng-disabled="ctrl.disabled"></gf-form-switch>
|
|
||||||
|
|
||||||
<div class="gf-form" ng-if="threshold.fill && threshold.colorMode === 'custom'">
|
|
||||||
<label class="gf-form-label">Fill color</label>
|
|
||||||
<span class="gf-form-label">
|
|
||||||
<color-picker color="threshold.fillColor" onChange="ctrl.onFillColorChange($index)"></color-picker>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<gf-form-switch class="gf-form" label="Line" checked="threshold.line"
|
|
||||||
on-change="ctrl.render()" ng-disabled="ctrl.disabled"></gf-form-switch>
|
|
||||||
|
|
||||||
<div class="gf-form" ng-if="threshold.line && threshold.colorMode === 'custom'">
|
|
||||||
<label class="gf-form-label">Line color</label>
|
|
||||||
<span class="gf-form-label">
|
|
||||||
<color-picker color="threshold.lineColor" onChange="ctrl.onLineColorChange($index)"></color-picker>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="gf-form">
|
|
||||||
<label class="gf-form-label">Y-Axis</label>
|
|
||||||
<div class="gf-form-select-wrapper">
|
|
||||||
<select class="gf-form-input" ng-model="threshold.yaxis"
|
|
||||||
ng-init="threshold.yaxis = threshold.yaxis === 'left' || threshold.yaxis === 'right' ? threshold.yaxis : 'left'"
|
|
||||||
ng-options="f for f in ['left', 'right']" ng-change="ctrl.render()" ng-disabled="ctrl.disabled">
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="gf-form">
|
|
||||||
<label class="gf-form-label">
|
|
||||||
<a class="pointer" ng-click="ctrl.removeThreshold($index)" ng-disabled="ctrl.disabled">
|
|
||||||
<i class="fa fa-trash"></i>
|
|
||||||
</a>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="gf-form-button-row">
|
|
||||||
<button class="btn btn-inverse" ng-click="ctrl.addThreshold()" ng-disabled="ctrl.disabled">
|
|
||||||
<i class="fa fa-plus"></i> Add Threshold
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
|
|
||||||
coreModule.directive('graphThresholdForm', () => {
|
coreModule.directive('graphThresholdForm', () => {
|
||||||
return {
|
return {
|
||||||
restrict: 'E',
|
restrict: 'E',
|
||||||
template: template,
|
templateUrl: 'public/app/plugins/panel/graph/thresholds_form.html',
|
||||||
controller: ThresholdFormCtrl,
|
controller: ThresholdFormCtrl,
|
||||||
bindToController: true,
|
bindToController: true,
|
||||||
controllerAs: 'ctrl',
|
controllerAs: 'ctrl',
|
||||||
|
248
public/app/plugins/panel/graph/time_region_manager.ts
Normal file
248
public/app/plugins/panel/graph/time_region_manager.ts
Normal file
@ -0,0 +1,248 @@
|
|||||||
|
import 'vendor/flot/jquery.flot';
|
||||||
|
import _ from 'lodash';
|
||||||
|
import moment from 'moment';
|
||||||
|
import config from 'app/core/config';
|
||||||
|
|
||||||
|
export const colorModes = {
|
||||||
|
gray: {
|
||||||
|
themeDependent: true,
|
||||||
|
title: 'Gray',
|
||||||
|
darkColor: { fill: 'rgba(255, 255, 255, 0.09)', line: 'rgba(255, 255, 255, 0.2)' },
|
||||||
|
lightColor: { fill: 'rgba(0, 0, 0, 0.09)', line: 'rgba(0, 0, 0, 0.2)' },
|
||||||
|
},
|
||||||
|
red: {
|
||||||
|
title: 'Red',
|
||||||
|
color: { fill: 'rgba(234, 112, 112, 0.12)', line: 'rgba(237, 46, 24, 0.60)' },
|
||||||
|
},
|
||||||
|
green: {
|
||||||
|
title: 'Green',
|
||||||
|
color: { fill: 'rgba(11, 237, 50, 0.090)', line: 'rgba(6,163,69, 0.60)' },
|
||||||
|
},
|
||||||
|
blue: {
|
||||||
|
title: 'Blue',
|
||||||
|
color: { fill: 'rgba(11, 125, 238, 0.12)', line: 'rgba(11, 125, 238, 0.60)' },
|
||||||
|
},
|
||||||
|
yellow: {
|
||||||
|
title: 'Yellow',
|
||||||
|
color: { fill: 'rgba(235, 138, 14, 0.12)', line: 'rgba(247, 149, 32, 0.60)' },
|
||||||
|
},
|
||||||
|
custom: { title: 'Custom' },
|
||||||
|
};
|
||||||
|
|
||||||
|
export function getColorModes() {
|
||||||
|
return _.map(Object.keys(colorModes), key => {
|
||||||
|
return {
|
||||||
|
key: key,
|
||||||
|
value: colorModes[key].title,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function getColor(timeRegion) {
|
||||||
|
if (Object.keys(colorModes).indexOf(timeRegion.colorMode) === -1) {
|
||||||
|
timeRegion.colorMode = 'red';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (timeRegion.colorMode === 'custom') {
|
||||||
|
return {
|
||||||
|
fill: timeRegion.fillColor,
|
||||||
|
line: timeRegion.lineColor,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const colorMode = colorModes[timeRegion.colorMode];
|
||||||
|
if (colorMode.themeDependent === true) {
|
||||||
|
return config.bootData.user.lightTheme ? colorMode.lightColor : colorMode.darkColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
return colorMode.color;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class TimeRegionManager {
|
||||||
|
plot: any;
|
||||||
|
timeRegions: any;
|
||||||
|
|
||||||
|
constructor(private panelCtrl) {}
|
||||||
|
|
||||||
|
draw(plot) {
|
||||||
|
this.timeRegions = this.panelCtrl.panel.timeRegions;
|
||||||
|
this.plot = plot;
|
||||||
|
}
|
||||||
|
|
||||||
|
addFlotOptions(options, panel) {
|
||||||
|
if (!panel.timeRegions || panel.timeRegions.length === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const tRange = { from: moment(this.panelCtrl.range.from).utc(), to: moment(this.panelCtrl.range.to).utc() };
|
||||||
|
|
||||||
|
let i, hRange, timeRegion, regions, fromStart, fromEnd, timeRegionColor;
|
||||||
|
|
||||||
|
const timeRegionsCopy = panel.timeRegions.map(a => ({ ...a }));
|
||||||
|
|
||||||
|
for (i = 0; i < timeRegionsCopy.length; i++) {
|
||||||
|
timeRegion = timeRegionsCopy[i];
|
||||||
|
|
||||||
|
if (!(timeRegion.fromDayOfWeek || timeRegion.from) && !(timeRegion.toDayOfWeek || timeRegion.to)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
hRange = {
|
||||||
|
from: this.parseTimeRange(timeRegion.from),
|
||||||
|
to: this.parseTimeRange(timeRegion.to),
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!timeRegion.fromDayOfWeek && timeRegion.toDayOfWeek) {
|
||||||
|
timeRegion.fromDayOfWeek = timeRegion.toDayOfWeek;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!timeRegion.toDayOfWeek && timeRegion.fromDayOfWeek) {
|
||||||
|
timeRegion.toDayOfWeek = timeRegion.fromDayOfWeek;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (timeRegion.fromDayOfWeek) {
|
||||||
|
hRange.from.dayOfWeek = Number(timeRegion.fromDayOfWeek);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (timeRegion.toDayOfWeek) {
|
||||||
|
hRange.to.dayOfWeek = Number(timeRegion.toDayOfWeek);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!hRange.from.h && hRange.to.h) {
|
||||||
|
hRange.from = hRange.to;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hRange.from.h && !hRange.to.h) {
|
||||||
|
hRange.to = hRange.from;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hRange.from.dayOfWeek && !hRange.from.h && !hRange.from.m) {
|
||||||
|
hRange.from.h = 0;
|
||||||
|
hRange.from.m = 0;
|
||||||
|
hRange.from.s = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hRange.to.dayOfWeek && !hRange.to.h && !hRange.to.m) {
|
||||||
|
hRange.to.h = 23;
|
||||||
|
hRange.to.m = 59;
|
||||||
|
hRange.to.s = 59;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!hRange.from || !hRange.to) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
regions = [];
|
||||||
|
|
||||||
|
if (
|
||||||
|
hRange.from.h >= tRange.from.hour() &&
|
||||||
|
hRange.from.h <= tRange.from.hour() &&
|
||||||
|
hRange.from.m >= tRange.from.minute() &&
|
||||||
|
hRange.from.m <= tRange.from.minute() &&
|
||||||
|
hRange.to.h >= tRange.to.hour() &&
|
||||||
|
hRange.to.h <= tRange.to.hour() &&
|
||||||
|
hRange.to.m >= tRange.to.minute() &&
|
||||||
|
hRange.to.m <= tRange.to.minute()
|
||||||
|
) {
|
||||||
|
regions.push({ from: tRange.from.valueOf(), to: tRange.to.startOf('hour').valueOf() });
|
||||||
|
} else {
|
||||||
|
fromStart = moment(tRange.from);
|
||||||
|
fromStart.set('hour', 0);
|
||||||
|
fromStart.set('minute', 0);
|
||||||
|
fromStart.set('second', 0);
|
||||||
|
fromStart.add(hRange.from.h, 'hours');
|
||||||
|
fromStart.add(hRange.from.m, 'minutes');
|
||||||
|
fromStart.add(hRange.from.s, 'seconds');
|
||||||
|
|
||||||
|
while (fromStart.unix() <= tRange.to.unix()) {
|
||||||
|
while (hRange.from.dayOfWeek && hRange.from.dayOfWeek !== fromStart.isoWeekday()) {
|
||||||
|
fromStart.add(24, 'hours');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fromStart.unix() > tRange.to.unix()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
fromEnd = moment(fromStart);
|
||||||
|
|
||||||
|
if (hRange.from.h <= hRange.to.h) {
|
||||||
|
fromEnd.add(hRange.to.h - hRange.from.h, 'hours');
|
||||||
|
} else if (hRange.from.h + hRange.to.h < 23) {
|
||||||
|
fromEnd.add(hRange.to.h, 'hours');
|
||||||
|
} else {
|
||||||
|
fromEnd.add(24 - hRange.from.h, 'hours');
|
||||||
|
}
|
||||||
|
|
||||||
|
fromEnd.set('minute', hRange.to.m);
|
||||||
|
fromEnd.set('second', hRange.to.s);
|
||||||
|
|
||||||
|
while (hRange.to.dayOfWeek && hRange.to.dayOfWeek !== fromEnd.isoWeekday()) {
|
||||||
|
fromEnd.add(24, 'hours');
|
||||||
|
}
|
||||||
|
|
||||||
|
const outsideRange =
|
||||||
|
(fromStart.unix() < tRange.from.unix() && fromEnd.unix() < tRange.from.unix()) ||
|
||||||
|
(fromStart.unix() > tRange.to.unix() && fromEnd.unix() > tRange.to.unix());
|
||||||
|
|
||||||
|
if (!outsideRange) {
|
||||||
|
regions.push({ from: fromStart.valueOf(), to: fromEnd.valueOf() });
|
||||||
|
}
|
||||||
|
|
||||||
|
fromStart.add(24, 'hours');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
timeRegionColor = getColor(timeRegion);
|
||||||
|
|
||||||
|
for (let j = 0; j < regions.length; j++) {
|
||||||
|
const r = regions[j];
|
||||||
|
if (timeRegion.fill) {
|
||||||
|
options.grid.markings.push({
|
||||||
|
xaxis: { from: r.from, to: r.to },
|
||||||
|
color: timeRegionColor.fill,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (timeRegion.line) {
|
||||||
|
options.grid.markings.push({
|
||||||
|
xaxis: { from: r.from, to: r.from },
|
||||||
|
color: timeRegionColor.line,
|
||||||
|
});
|
||||||
|
options.grid.markings.push({
|
||||||
|
xaxis: { from: r.to, to: r.to },
|
||||||
|
color: timeRegionColor.line,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
parseTimeRange(str) {
|
||||||
|
const timeRegex = /^([\d]+):?(\d{2})?/;
|
||||||
|
const result = { h: null, m: null };
|
||||||
|
const match = timeRegex.exec(str);
|
||||||
|
|
||||||
|
if (!match) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (match.length > 1) {
|
||||||
|
result.h = Number(match[1]);
|
||||||
|
result.m = 0;
|
||||||
|
|
||||||
|
if (match.length > 2 && match[2] !== undefined) {
|
||||||
|
result.m = Number(match[2]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result.h > 23) {
|
||||||
|
result.h = 23;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result.m > 59) {
|
||||||
|
result.m = 59;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
64
public/app/plugins/panel/graph/time_regions_form.html
Normal file
64
public/app/plugins/panel/graph/time_regions_form.html
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
<div class="gf-form-group">
|
||||||
|
<h5>Time regions <tip>All configured time regions refers to UTC time</tip></h5>
|
||||||
|
<div class="gf-form-inline" ng-repeat="timeRegion in ctrl.panel.timeRegions">
|
||||||
|
<div class="gf-form">
|
||||||
|
<label class="gf-form-label">T{{$index+1}}</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="gf-form">
|
||||||
|
<label class="gf-form-label">From</label>
|
||||||
|
<div class="gf-form-select-wrapper">
|
||||||
|
<select class="gf-form-input width-6" ng-model="timeRegion.fromDayOfWeek" ng-options="f.d as f.value for f in [{d: undefined, value: 'Any'}, {d:1, value: 'Mon'}, {d:2, value: 'Tue'}, {d:3, value: 'Wed'}, {d:4, value: 'Thu'}, {d:5, value: 'Fri'}, {d:6, value: 'Sat'}, {d:7, value: 'Sun'}]"
|
||||||
|
ng-change="ctrl.render()"></select>
|
||||||
|
</div>
|
||||||
|
<input type="text" ng-maxlength="5" ng-model="timeRegion.from" class="gf-form-input width-5" ng-change="ctrl.render()" placeholder="hh:mm">
|
||||||
|
<label class="gf-form-label">To</label>
|
||||||
|
<div class="gf-form-select-wrapper">
|
||||||
|
<select class="gf-form-input width-6" ng-model="timeRegion.toDayOfWeek" ng-options="f.d as f.value for f in [{d: undefined, value: 'Any'}, {d:1, value: 'Mon'}, {d:2, value: 'Tue'}, {d:3, value: 'Wed'}, {d:4, value: 'Thu'}, {d:5, value: 'Fri'}, {d:6, value: 'Sat'}, {d:7, value: 'Sun'}]"
|
||||||
|
ng-change="ctrl.render()"></select>
|
||||||
|
</div>
|
||||||
|
<input type="text" ng-maxlength="5" ng-model="timeRegion.to" class="gf-form-input width-5" ng-change="ctrl.render()" placeholder="hh:mm"
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="gf-form">
|
||||||
|
<label class="gf-form-label">Color</label>
|
||||||
|
<div class="gf-form-select-wrapper">
|
||||||
|
<select class="gf-form-input" ng-model="timeRegion.colorMode" ng-options="f.key as f.value for f in ctrl.colorModes" ng-change="ctrl.render()">
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<gf-form-switch class="gf-form" label="Fill" checked="timeRegion.fill" on-change="ctrl.render()"></gf-form-switch>
|
||||||
|
|
||||||
|
<div class="gf-form" ng-if="timeRegion.fill && timeRegion.colorMode === 'custom'">
|
||||||
|
<label class="gf-form-label">Fill color</label>
|
||||||
|
<span class="gf-form-label">
|
||||||
|
<color-picker color="timeRegion.fillColor" onChange="ctrl.onFillColorChange($index)"></color-picker>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<gf-form-switch class="gf-form" label="Line" checked="timeRegion.line" on-change="ctrl.render()"></gf-form-switch>
|
||||||
|
|
||||||
|
<div class="gf-form" ng-if="timeRegion.line && timeRegion.colorMode === 'custom'">
|
||||||
|
<label class="gf-form-label">Line color</label>
|
||||||
|
<span class="gf-form-label">
|
||||||
|
<color-picker color="timeRegion.lineColor" onChange="ctrl.onLineColorChange($index)"></color-picker>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="gf-form">
|
||||||
|
<label class="gf-form-label">
|
||||||
|
<a class="pointer" ng-click="ctrl.removeTimeRegion($index)">
|
||||||
|
<i class="fa fa-trash"></i>
|
||||||
|
</a>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="gf-form-button-row">
|
||||||
|
<button class="btn btn-inverse" ng-click="ctrl.addTimeRegion()">
|
||||||
|
<i class="fa fa-plus"></i> Add time region
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
73
public/app/plugins/panel/graph/time_regions_form.ts
Normal file
73
public/app/plugins/panel/graph/time_regions_form.ts
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
import coreModule from 'app/core/core_module';
|
||||||
|
import { getColorModes } from './time_region_manager';
|
||||||
|
|
||||||
|
export class TimeRegionFormCtrl {
|
||||||
|
panelCtrl: any;
|
||||||
|
panel: any;
|
||||||
|
disabled: boolean;
|
||||||
|
colorModes: any;
|
||||||
|
|
||||||
|
/** @ngInject */
|
||||||
|
constructor($scope) {
|
||||||
|
this.panel = this.panelCtrl.panel;
|
||||||
|
|
||||||
|
const unbindDestroy = $scope.$on('$destroy', () => {
|
||||||
|
this.panelCtrl.editingTimeRegions = false;
|
||||||
|
this.panelCtrl.render();
|
||||||
|
unbindDestroy();
|
||||||
|
});
|
||||||
|
|
||||||
|
this.colorModes = getColorModes();
|
||||||
|
this.panelCtrl.editingTimeRegions = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
this.panelCtrl.render();
|
||||||
|
}
|
||||||
|
|
||||||
|
addTimeRegion() {
|
||||||
|
this.panel.timeRegions.push({
|
||||||
|
op: 'time',
|
||||||
|
fromDayOfWeek: undefined,
|
||||||
|
from: undefined,
|
||||||
|
toDayOfWeek: undefined,
|
||||||
|
to: undefined,
|
||||||
|
colorMode: 'background6',
|
||||||
|
fill: true,
|
||||||
|
line: false,
|
||||||
|
});
|
||||||
|
this.panelCtrl.render();
|
||||||
|
}
|
||||||
|
|
||||||
|
removeTimeRegion(index) {
|
||||||
|
this.panel.timeRegions.splice(index, 1);
|
||||||
|
this.panelCtrl.render();
|
||||||
|
}
|
||||||
|
|
||||||
|
onFillColorChange(index) {
|
||||||
|
return newColor => {
|
||||||
|
this.panel.timeRegions[index].fillColor = newColor;
|
||||||
|
this.render();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
onLineColorChange(index) {
|
||||||
|
return newColor => {
|
||||||
|
this.panel.timeRegions[index].lineColor = newColor;
|
||||||
|
this.render();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
coreModule.directive('graphTimeRegionForm', () => {
|
||||||
|
return {
|
||||||
|
restrict: 'E',
|
||||||
|
templateUrl: 'public/app/plugins/panel/graph/time_regions_form.html',
|
||||||
|
controller: TimeRegionFormCtrl,
|
||||||
|
bindToController: true,
|
||||||
|
controllerAs: 'ctrl',
|
||||||
|
scope: {
|
||||||
|
panelCtrl: '=',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
});
|
Loading…
Reference in New Issue
Block a user