Merge remote-tracking branch 'upstream/master'

This commit is contained in:
Alin Sinpalean
2017-09-12 21:57:32 +02:00
42 changed files with 239 additions and 127 deletions
@@ -41,7 +41,9 @@ Proxy access means that the Grafana backend will proxy all requests from the bro
Click the ``Select metric`` link to start navigating the metric space. One you start you can continue using the mouse
or keyboard arrow keys. You can select a wildcard and still continue.
![](/img/docs/animated_gifs/graphite_query1.gif)
{{< docs-imagebox img="/img/docs/v45/graphite_query1_still.png" class="docs-image--center"
animated-gif="/img/docs/v45/graphite_query1.gif" >}}
### Functions
@@ -50,13 +52,17 @@ a function is selected it will be added and your focus will be in the text box o
a parameter just click on it and it will turn into a text box. To delete a function click the function name followed
by the x icon.
![](/img/docs/animated_gifs/graphite_query2.gif)
{{< docs-imagebox img="/img/docs/v45/graphite_query2_still.png" class="docs-image--center"
animated-gif="/img/docs/v45/graphite_query2.gif" >}}
### Optional parameters
Some functions like aliasByNode support an optional second argument. To add this parameter specify for example 3,-2 as the first parameter and the function editor will adapt and move the -2 to a second parameter. To remove the second optional parameter just click on it and leave it blank and the editor will remove it.
![](/img/docs/animated_gifs/func_editor_optional_params.gif)
{{< docs-imagebox img="/img/docs/v45/graphite_query3_still.png" class="docs-image--center"
animated-gif="/img/docs/v45/graphite_query3.gif" >}}
### Nested Queries
@@ -41,7 +41,9 @@ mode is also more secure as the username & password will never reach the browser
## Query Editor
![](/assets/img/blog/v2.6/influxdb_editor_v3.gif)
{{< docs-imagebox img="/img/docs/v45/influxdb_query_still.png" class="docs-image--center"
animated-gif="/img/docs/v45/influxdb_query.gif" >}}
You find the InfluxDB editor in the metrics tab in Graph or Singlestat panel's edit mode. You enter edit mode by clicking the
panel title, then edit. The editor allows you to select metrics and tags.
+2 -6
View File
@@ -50,15 +50,11 @@ populate the template variable to a desired value from the link.
The metrics tab defines what series data and sources to render. Each datasource provides different
options.
## Axes & Grid
## Axes
![](/img/docs/v43/graph_axes_grid_options.png)
The Axes & Grid tab controls the display of axes, grids and legend.
### Axes
The ``Left Y`` and ``Right Y`` can be customized using:
The Axes tab controls the display of axes, grids and legend. The ``Left Y`` and ``Right Y`` can be customized using:
- ``Unit`` - The display unit for the Y value
- ``Grid Max`` - The maximum Y value. (default auto)
+1 -1
View File
@@ -27,7 +27,7 @@ this folder to anywhere you want Grafana to run from. Go into the
The default Grafana port is `3000`, this port requires extra permissions
on windows. Edit `custom.ini` and uncomment the `http_port`
configuration option and change it to something like `8080` or similar.
configuration option (`;` is the comment character in ini files) and change it to something like `8080` or similar.
That port should not require extra Windows privileges.
Start Grafana by executing `grafana-server.exe`, preferably from the
+22
View File
@@ -43,3 +43,25 @@ Playlists can also be manually controlled utilizing the Playlist controls at the
Click the stop button to stop the Playlist, and exit to the current Dashboard.
Click the next button to advance to the next Dashboard in the Playlist.
Click the back button to rewind to the previous Dashboard in the Playlist.
## TV or Kiosk Mode
In TV mode the top navbar, row & panel controls will all fade to transparent.
This happens automatically after one minute of user inactivity but can also be toggled manually
with the `d v` sequence shortcut. Any mouse movement or keyboard action will
restore navbar & controls.
Another feature is the kiosk mode - in kiosk mode the navbar is completely hidden/removed from view. This can be enabled with the `d k`
shortcut.
To put a playlist into kiosk mode, use the `d k` shortcut after the playlist has started. The same shortcut will toggle the playlist out of kiosk mode.
### Linking to the Playlist in Kiosk Mode
If you want to create a link to the playlist with kiosk mode enabled:
1. Copy the Start Url (by right clicking on the Play button and choosing Copy link address).
2. Add the `?kiosk` parameter to the url.
For example, to open the first playlist on the Grafana Play site in kiosk mode: [http://play.grafana.org/playlists/play/1?kiosk](http://play.grafana.org/playlists/play/1?kiosk)
-4
View File
@@ -53,10 +53,6 @@
"systemjs": "0.19.41",
"zone.js": "^0.7.2"
},
"engines": {
"node": "4.x",
"npm": "2.14.x"
},
"scripts": {
"build": "./node_modules/grunt-cli/bin/grunt",
"test": "./node_modules/grunt-cli/bin/grunt test",
+2
View File
@@ -78,6 +78,7 @@ func init() {
"AWS/Lambda": {"Invocations", "Errors", "Duration", "Throttles", "IteratorAge"},
"AWS/Logs": {"IncomingBytes", "IncomingLogEvents", "ForwardedBytes", "ForwardedLogEvents", "DeliveryErrors", "DeliveryThrottling"},
"AWS/ML": {"PredictCount", "PredictFailureCount"},
"AWS/NATGateway": {"PacketsOutToDestination", "PacketsOutToSource", "PacketsInFromSource", "PacketsInFromDestination", "BytesOutToDestination", "BytesOutToSource", "BytesInFromSource", "BytesInFromDestination", "ErrorPortAllocation", "ActiveConnectionCount", "ConnectionAttemptCount", "ConnectionEstablishedCount", "IdleTimeoutCount", "PacketsDropCount"},
"AWS/OpsWorks": {"cpu_idle", "cpu_nice", "cpu_system", "cpu_user", "cpu_waitio", "load_1", "load_5", "load_15", "memory_buffers", "memory_cached", "memory_free", "memory_swap", "memory_total", "memory_used", "procs"},
"AWS/Redshift": {"CPUUtilization", "DatabaseConnections", "HealthStatus", "MaintenanceMode", "NetworkReceiveThroughput", "NetworkTransmitThroughput", "PercentageDiskSpaceUsed", "ReadIOPS", "ReadLatency", "ReadThroughput", "WriteIOPS", "WriteLatency", "WriteThroughput"},
"AWS/RDS": {"ActiveTransactions", "AuroraBinlogReplicaLag", "AuroraReplicaLag", "AuroraReplicaLagMaximum", "AuroraReplicaLagMinimum", "BinLogDiskUsage", "BlockedTransactions", "BufferCacheHitRatio", "CommitLatency", "CommitThroughput", "BinLogDiskUsage", "CPUCreditBalance", "CPUCreditUsage", "CPUUtilization", "DatabaseConnections", "DDLLatency", "DDLThroughput", "Deadlocks", "DeleteLatency", "DeleteThroughput", "DiskQueueDepth", "DMLLatency", "DMLThroughput", "EngineUptime", "FailedSqlStatements", "FreeableMemory", "FreeLocalStorage", "FreeStorageSpace", "InsertLatency", "InsertThroughput", "LoginFailures", "NetworkReceiveThroughput", "NetworkTransmitThroughput", "NetworkThroughput", "Queries", "ReadIOPS", "ReadLatency", "ReadThroughput", "ReplicaLag", "ResultSetCacheHitRatio", "SelectLatency", "SelectThroughput", "SwapUsage", "TotalConnections", "UpdateLatency", "UpdateThroughput", "VolumeBytesUsed", "VolumeReadIOPS", "VolumeWriteIOPS", "WriteIOPS", "WriteLatency", "WriteThroughput"},
@@ -122,6 +123,7 @@ func init() {
"AWS/Lambda": {"FunctionName", "Resource", "Version", "Alias"},
"AWS/Logs": {"LogGroupName", "DestinationType", "FilterName"},
"AWS/ML": {"MLModelId", "RequestMode"},
"AWS/NATGateway": {"NatGatewayId"},
"AWS/OpsWorks": {"StackId", "LayerId", "InstanceId"},
"AWS/Redshift": {"NodeID", "ClusterIdentifier"},
"AWS/RDS": {"DBInstanceIdentifier", "DBClusterIdentifier", "DatabaseClass", "EngineName", "Role"},
+1 -1
View File
@@ -29,7 +29,7 @@ func QueryMetrics(c *middleware.Context, reqDto dtos.MetricRequest) Response {
return ApiError(400, "Query missing datasourceId", nil)
}
dsQuery := models.GetDataSourceByIdQuery{Id: dsId}
dsQuery := models.GetDataSourceByIdQuery{Id: dsId, OrgId: c.OrgId}
if err := bus.Dispatch(&dsQuery); err != nil {
return ApiError(500, "failed to fetch data source", err)
}
@@ -5,6 +5,8 @@ import (
"strings"
"time"
gocontext "context"
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/components/null"
"github.com/grafana/grafana/pkg/components/simplejson"
@@ -112,6 +114,10 @@ func (c *QueryCondition) executeQuery(context *alerting.EvalContext, timeRange *
resp, err := c.HandleRequest(context.Ctx, req)
if err != nil {
if err == gocontext.DeadlineExceeded {
return nil, fmt.Errorf("Alert execution exceeded the timeout")
}
return nil, fmt.Errorf("tsdb.HandleRequest() error %v", err)
}
+3 -2
View File
@@ -27,12 +27,13 @@ func Decrypt(payload []byte, secret string) ([]byte, error) {
}
iv := payload[saltLength : saltLength+aes.BlockSize]
payload = payload[saltLength+aes.BlockSize:]
payloadDst := make([]byte, len(payload))
stream := cipher.NewCFBDecrypter(block, iv)
// XORKeyStream can work in-place if the two arguments are the same.
stream.XORKeyStream(payload, payload)
return payload, nil
stream.XORKeyStream(payloadDst, payload)
return payloadDst, nil
}
func Encrypt(payload []byte, secret string) ([]byte, error) {
@@ -111,6 +111,17 @@ function link(scope, elem, attrs) {
let textarea = elem.find("textarea");
textarea.addClass('gf-form-input');
if (scope.codeEditorFocus) {
setTimeout(function () {
textarea.focus();
var domEl = textarea[0];
if (domEl.setSelectionRange) {
var pos = textarea.val().length * 2;
domEl.setSelectionRange(pos, pos);
}
}, 100);
}
// Event handlers
editorSession.on('change', (e) => {
scope.$apply(() => {
@@ -199,6 +210,7 @@ export function codeEditorDirective() {
template: editorTemplate,
scope: {
content: "=",
codeEditorFocus: "<",
onChange: "&",
getCompleter: "&"
},
@@ -65,20 +65,20 @@ var PrometheusHighlightRules = function() {
regex : "\\s+"
} ],
"start-label-matcher" : [ {
token : "label.name",
token : "keyword",
regex : '[a-zA-Z_][a-zA-Z0-9_]*'
}, {
token : "label.matching_operator",
regex : '=|!=|=~|!~'
token : "keyword.operator",
regex : '=~|=|!~|!='
}, {
token : "label.value",
token : "string",
regex : '"[^"]*"|\'[^\']*\''
}, {
token : "label.matching_delimiter",
token : "punctuation.operator",
regex : ",",
push : 'start-label-matcher'
}, {
token : "label.matching_end",
token : "paren.rparen",
regex : "}",
next : "start"
} ]
+10 -1
View File
@@ -16,7 +16,7 @@ export class AlertSrv {
init() {
this.$rootScope.onAppEvent('alert-error', (e, alert) => {
this.set(alert[0], alert[1], 'error', 7000);
this.set(alert[0], alert[1], 'error', 12000);
}, this.$rootScope);
this.$rootScope.onAppEvent('alert-warning', (e, alert) => {
@@ -33,6 +33,14 @@ export class AlertSrv {
appEvents.on('confirm-modal', this.showConfirmModal.bind(this));
}
getIconForSeverity(severity) {
switch (severity) {
case 'success': return 'fa fa-check';
case 'error': return 'fa fa-exclamation-triangle';
default: return 'fa fa-exclamation';
}
}
set(title, text, severity, timeout) {
if (_.isObject(text)) {
console.log('alert error', text);
@@ -45,6 +53,7 @@ export class AlertSrv {
title: title || '',
text: text || '',
severity: severity || 'info',
icon: this.getIconForSeverity(severity)
};
var newAlertJson = angular.toJson(newAlert);
+8 -2
View File
@@ -64,7 +64,13 @@ export class BackendSrv {
}
if (data.message) {
this.alertSrv.set("Problem!", data.message, data.severity, 10000);
let description = "";
let message = data.message;
if (message.length > 80) {
description = message;
message = "Error";
}
this.alertSrv.set(message, description, data.severity, 10000);
}
throw data;
@@ -97,7 +103,7 @@ export class BackendSrv {
return results.data;
}, err => {
// handle unauthorized
if (err.status === 401 && firstAttempt) {
if (err.status === 401 && this.contextSrv.user.isSignedIn && firstAttempt) {
return this.loginPing().then(() => {
options.retry = 1;
return this.request(options);
+3 -6
View File
@@ -88,19 +88,16 @@ function($, _) {
// 9h
case (interval < 32400000):
return 21600000; // 6h
// 24h
// 1d
case (interval < 86400000):
return 43200000; // 12h
// 48h
case (interval < 172800000):
return 86400000; // 24h
// 1w
case (interval < 604800000):
return 86400000; // 24h
return 86400000; // 1d
// 3w
case (interval < 1814400000):
return 604800000; // 1w
// 2y
// 6w
case (interval < 3628800000):
return 2592000000; // 30d
default:
@@ -83,7 +83,7 @@ export class DashboardSrv {
}
this.$rootScope.appEvent('dashboard-saved', this.dash);
this.$rootScope.appEvent('alert-success', ['Dashboard saved', 'Saved as ' + clone.title]);
this.$rootScope.appEvent('alert-success', ['Dashboard saved']);
}
save(clone, options) {
+2
View File
@@ -10,9 +10,11 @@ export class QueryCtrl {
panel: any;
hasRawMode: boolean;
error: string;
isLastQuery: boolean;
constructor(public $scope, private $injector) {
this.panel = this.panelCtrl.panel;
this.isLastQuery = _.indexOf(this.panel.targets, this.target) === (this.panel.targets.length - 1);
}
refresh() {
+2 -5
View File
@@ -126,21 +126,18 @@ export class DataSourceEditCtrl {
return;
}
this.testing = {done: false};
this.testing = {done: false, status: 'error'};
// make test call in no backend cache context
this.backendSrv.withNoBackendCache(() => {
return datasource.testDatasource().then(result => {
this.testing.message = result.message;
this.testing.status = result.status;
this.testing.title = result.title;
}).catch(err => {
if (err.statusText) {
this.testing.message = err.statusText;
this.testing.title = "HTTP Error";
this.testing.message = 'HTTP Error ' + err.statusText;
} else {
this.testing.message = err.message;
this.testing.title = "Unknown error";
}
});
}).finally(() => {
@@ -59,9 +59,14 @@
<div ng-if="ctrl.testing" class="gf-form-group">
<h5 ng-show="!ctrl.testing.done">Testing.... <i class="fa fa-spiner fa-spin"></i></h5>
<div class="alert-{{ctrl.testing.status}} alert">
<div class="alert-title">{{ctrl.testing.title}}</div>
<div ng-bind='ctrl.testing.message'></div>
<div class="alert-{{ctrl.testing.status}} alert" ng-show="ctrl.testing.done">
<div class="alert-icon">
<i class="fa fa-exclamation-triangle" ng-show="ctrl.testing.status === 'error'"></i>
<i class="fa fa-check" ng-show="ctrl.testing.status !== 'error'"></i>
</div>
<div class="alert-body">
<div class="alert-title">{{ctrl.testing.message}}</div>
</div>
</div>
</div>
@@ -335,7 +335,7 @@ function (angular, _, moment, dateMath, kbn, templatingVariable, CloudWatchAnnot
var dimensions = {};
return this.getDimensionValues(region, namespace, metricName, 'ServiceName', dimensions).then(function () {
return { status: 'success', message: 'Data source is working', title: 'Success' };
return { status: 'success', message: 'Data source is working' };
});
};
@@ -175,9 +175,9 @@ function (angular, _, moment, kbn, ElasticQueryBuilder, IndexPattern, ElasticRes
return this.getFields({type: 'date'}).then(function(dateFields) {
var timeField = _.find(dateFields, {text: this.timeField});
if (!timeField) {
return { status: "error", message: "No date field named " + this.timeField + ' found', title: "Error" };
return { status: "error", message: "No date field named " + this.timeField + ' found' };
}
return { status: "success", message: "Index OK. Time field name OK.", title: "Success" };
return { status: "success", message: "Index OK. Time field name OK." };
}.bind(this), function(err) {
console.log(err);
if (err.data && err.data.error) {
@@ -185,9 +185,9 @@ function (angular, _, moment, kbn, ElasticQueryBuilder, IndexPattern, ElasticRes
if (err.data.error.reason) {
message = err.data.error.reason;
}
return { status: "error", message: message, title: "Error" };
return { status: "error", message: message };
} else {
return { status: "error", message: err.status, title: "Error" };
return { status: "error", message: err.status };
}
});
};
@@ -205,7 +205,7 @@ export function GraphiteDatasource(instanceSettings, $q, backendSrv, templateSrv
this.testDatasource = function() {
return this.metricFindQuery('*').then(function () {
return { status: "success", message: "Data source is working", title: "Success" };
return { status: "success", message: "Data source is working"};
});
};
@@ -24,6 +24,11 @@ describe('GraphiteQueryCtrl', function() {
ctx.target = {target: 'aliasByNode(scaleToSeconds(test.prod.*,1),2)'};
ctx.datasource.metricFindQuery = sinon.stub().returns(ctx.$q.when([]));
ctx.panelCtrl = {panel: {}};
ctx.panelCtrl = {
panel: {
targets: [ctx.target]
}
};
ctx.panelCtrl.refresh = sinon.spy();
ctx.ctrl = $controller(GraphiteQueryCtrl, {$scope: ctx.scope}, {
@@ -196,11 +196,11 @@ export default class InfluxDatasource {
return this.metricFindQuery('SHOW DATABASES').then(res => {
let found = _.find(res, {text: this.database});
if (!found) {
return { status: "error", message: "Could not find the specified database name.", title: "DB Not found" };
return { status: "error", message: "Could not find the specified database name." };
}
return { status: "success", message: "Data source is working", title: "Success" };
return { status: "success", message: "Data source is working" };
}).catch(err => {
return { status: "error", message: err.message, title: "Test Failed" };
return { status: "error", message: err.message };
});
}
@@ -92,7 +92,7 @@
<div class="gf-form-inline" ng-if="ctrl.target.orderByTime === 'DESC'">
<div class="gf-form">
<label class="gf-form-label query-keyword width-7">ORDER BY</label>
<label class="gf-form-label pointer" ng-click="ctrl.removeOrderByTime()">time <span classs="query-keyword">DESC</span> <i class="fa fa-remove"></i></label>
<label class="gf-form-label pointer" ng-click="ctrl.removeOrderByTime()">time <span class="query-keyword">DESC</span> <i class="fa fa-remove"></i></label>
</div>
<div class="gf-form gf-form--grow">
<div class="gf-form-label gf-form-label--grow"></div>
@@ -19,9 +19,13 @@ describe('InfluxDBQueryCtrl', function() {
ctx.$q = $q;
ctx.scope = $rootScope.$new();
ctx.datasource.metricFindQuery = sinon.stub().returns(ctx.$q.when([]));
ctx.panelCtrl = {panel: {}};
ctx.panelCtrl.refresh = sinon.spy();
ctx.target = {target: {}};
ctx.panelCtrl = {
panel: {
targets: [ctx.target]
}
};
ctx.panelCtrl.refresh = sinon.spy();
ctx.ctrl = $controller(InfluxQueryCtrl, {$scope: ctx.scope}, {
panelCtrl: ctx.panelCtrl,
target: ctx.target,
@@ -118,13 +118,13 @@ export class MysqlDatasource {
}],
}
}).then(res => {
return { status: "success", message: "Database Connection OK", title: "Success" };
return { status: "success", message: "Database Connection OK"};
}).catch(err => {
console.log(err);
if (err.data && err.data.message) {
return { status: "error", message: err.data.message, title: "Error" };
return { status: "error", message: err.data.message };
} else {
return { status: "error", message: err.status, title: "Error" };
return { status: "error", message: err.status };
}
});
}
@@ -296,7 +296,7 @@ function (angular, _, dateMath) {
this.testDatasource = function() {
return this._performSuggestQuery('cpu', 'metrics').then(function () {
return { status: "success", message: "Data source is working", title: "Success" };
return { status: "success", message: "Data source is working" };
});
};
@@ -18,7 +18,11 @@ describe('OpenTsQueryCtrl', function() {
ctx.$q = $q;
ctx.scope = $rootScope.$new();
ctx.target = {target: ''};
ctx.panelCtrl = {panel: {}};
ctx.panelCtrl = {
panel: {
targets: [ctx.target]
}
};
ctx.panelCtrl.refresh = sinon.spy();
ctx.datasource.getAggregators = sinon.stub().returns(ctx.$q.when([]));
ctx.datasource.getFilterTypes = sinon.stub().returns(ctx.$q.when([]));
@@ -241,7 +241,7 @@ export class PrometheusDatasource {
testDatasource() {
return this.metricFindQuery('metrics(.*)').then(function() {
return { status: 'success', message: 'Data source is working', title: 'Success' };
return { status: 'success', message: 'Data source is working'};
});
}
@@ -2,7 +2,7 @@
<div class="gf-form-inline">
<div class="gf-form gf-form--grow">
<code-editor content="ctrl.target.expr" on-change="ctrl.refreshMetricData()"
get-completer="ctrl.getCompleter()" data-mode="prometheus">
get-completer="ctrl.getCompleter()" data-mode="prometheus" code-editor-focus="ctrl.isLastQuery">
</code-editor>
</div>
</div>
@@ -0,0 +1,27 @@
import {describe, beforeEach, it, sinon, expect} from 'test/lib/common';
import {PromCompleter} from '../completer';
import {PrometheusDatasource} from '../datasource';
describe('Prometheus editor completer', function() {
let editor = {};
let session = {
getTokenAt: sinon.stub().returns({}),
getLine: sinon.stub().returns(""),
};
let datasourceStub = <PrometheusDatasource>{};
let completer = new PromCompleter(datasourceStub);
describe("When inside brackets", () => {
it("Should return range vectors", () => {
completer.getCompletions(editor, session, 10, "[", (s, res) => {
expect(res[0]).to.eql({caption: '1s', value: '[1s', meta: 'range vector'});
});
});
});
});
+1 -1
View File
@@ -3,7 +3,7 @@
<div class="gf-form">
<span class="gf-form-label">Mode</span>
<span class="gf-form-select-wrapper">
<select class="gf-form-input" ng-model="ctrl.panel.mode" ng-options="f for f in ['html','markdown','text']"></select>
<select class="gf-form-input" ng-model="ctrl.panel.mode" ng-options="f for f in ['html','markdown']"></select>
</span>
</div>
</div>
+12 -12
View File
@@ -103,7 +103,7 @@ $tight-form-func-bg: #333;
$tight-form-func-highlight-bg: #444;
$modal-background: $black;
$code-tag-bg: $dark-5;
$code-tag-bg: $gray-1;
$code-tag-border: lighten($code-tag-bg, 2%);
@@ -238,17 +238,15 @@ $paginationActiveBackground: $blue;
// Form states and alerts
// -------------------------
$state-warning-text: $warn;
$state-warning-bg: $brand-warning;
$warning-text-color: $warn;
$error-text-color: #E84D4D;
$success-text-color: #12D95A;
$info-text-color: $blue-dark;
$errorText: #E84D4D;
$errorBackground: $btn-danger-bg;
$successText: #12D95A;
$successBackground: $btn-success-bg;
$infoText: $blue-dark;
$infoBackground: $blue-dark;
$alert-error-bg: linear-gradient(90deg, #d44939, #e0603d);
$alert-success-bg: linear-gradient(90deg, #3aa655, #47b274);
$alert-warning-bg: linear-gradient(90deg, #d44939, #e0603d);
$alert-info-bg: linear-gradient(100deg, #1a4552, #00374a);
// popover
$popover-bg: $panel-bg;
@@ -258,6 +256,8 @@ $popover-border-color: $gray-1;
$popover-help-bg: $btn-secondary-bg;
$popover-help-color: $text-color;
$popover-error-bg: $btn-danger-bg;
// Tooltips and popovers
// -------------------------
$tooltipColor: $popover-help-color;
@@ -276,7 +276,7 @@ $card-background-hover: linear-gradient(135deg, #343434, #262626);
$card-shadow: -1px -1px 0 0 hsla(0, 0%, 100%, .1), 1px 1px 0 0 rgba(0, 0, 0, .3);
// info box
$info-box-background: linear-gradient(100deg, #1a4552, #0b2127);
$info-box-background: linear-gradient(100deg, #1a4552, #00374a);
// footer
$footer-link-color: $gray-1;
+9 -15
View File
@@ -259,29 +259,23 @@ $paginationActiveBackground: $blue;
// Form states and alerts
// -------------------------
$state-warning-text: lighten($orange, 10%);
$state-warning-bg: $orange;
$warningBorder: transparent;
$warning-text-color: lighten($orange, 10%);
$error-text-color: lighten($red, 10%);
$success-text-color: lighten($green, 10%);
$info-text-color: $blue;
$errorText: lighten($red, 10%);
$errorBackground: $red;
$errorBorder: transparent;
$successText: lighten($green, 10%);
$successBackground: $green;
$successBorder: transparent;
$infoText: $blue;
$infoBackground: $blue-dark;
$infoBorder: transparent;
$alert-error-bg: linear-gradient(90deg, #d44939, #e0603d);
$alert-success-bg: linear-gradient(90deg, #3aa655, #47b274);
$alert-warning-bg: linear-gradient(90deg, #d44939, #e0603d);
$alert-info-bg: $blue-dark;
// popover
$popover-bg: $gray-5;
$popover-color: $text-color;
$popover-border-color: $gray-3;
$popover-help-bg: $blue-dark;
$popover-help-color: $gray-6;
$popover-error-bg: $btn-danger-bg;
// Tooltips and popovers
// -------------------------
+9 -9
View File
@@ -31,21 +31,21 @@ cite { font-style: normal; }
a.muted:hover,
a.muted:focus { color: darken($text-muted, 10%); }
.text-warning { color: $state-warning-text; }
.text-warning { color: $warning-text-color; }
a.text-warning:hover,
a.text-warning:focus { color: darken($state-warning-text, 10%); }
a.text-warning:focus { color: darken($warning-text-color, 10%); }
.text-error { color: $errorText; }
.text-error { color: $error-text-color; }
a.text-error:hover,
a.text-error:focus { color: darken($errorText, 10%); }
a.text-error:focus { color: darken($error-text-color, 10%); }
.text-info { color: $infoText; }
.text-info { color: $info-text-color; }
a.text-info:hover,
a.text-info:focus { color: darken($infoText, 10%); }
a.text-info:focus { color: darken($info-text-color, 10%); }
.text-success { color: $successText; }
.text-success { color: $success-text-color; }
a.text-success:hover,
a.text-success:focus { color: darken($successText, 10%); }
a.text-success:focus { color: darken($success-text-color, 10%); }
a { cursor: pointer; }
a[disabled] {
@@ -130,7 +130,7 @@ small,
mark,
.mark {
padding: .2em;
background-color: $state-warning-bg;
background: $alert-warning-bg;
}
+34 -22
View File
@@ -7,60 +7,57 @@
// -------------------------
.alert {
padding: 0.5rem 2rem 0.5rem 1rem;
padding: 1.25rem 2rem 1.25rem 1.5rem;
margin-bottom: $line-height-base;
text-shadow: 0 1px 0 rgba(255,255,255,.5);
background-color: $state-warning-bg;
text-shadow: 0 2px 0 rgba(255,255,255,.5);
background: $alert-error-bg;
position: relative;
color: $white;
text-shadow: 0 1px 0 rgba(0,0,0,.5);
text-shadow: 0 1px 0 rgba(0,0,0,.2);
border-radius: 2px;
display: flex;
flex-direction: row;
}
// Alternate styles
// -------------------------
.alert-success {
background-color: $successBackground;
background: $alert-success-bg;
}
.alert-danger,
.alert-error {
background-color: $errorBackground;
background: $alert-error-bg;
}
.alert-info {
background-color: $infoBackground;
background: $alert-info-bg;
}
.alert-warning {
background-color: $state-warning-bg;
background: $alert-warning-bg;
}
.page-alert-list {
z-index: 8000;
min-width: 300px;
max-width: 300px;
min-width: 400px;
max-width: 600px;
position: fixed;
right: 20px;
top: 56px;
}
.alert-close {
position: absolute;
top: -4px;
right: -2px;
width: 16px;
height: 16px;
padding: 0;
background: $white;
border-radius: 50%;
padding: 0 0 0 1rem;
border: none;
font-size: 1.1rem;
color: $dark-4;
background: none;
display: flex;
align-items: center;
.fa {
position: relative;
top: -2px;
align-self: flex-end;
font-size: 1.5rem;
color: rgba(255,255,255,.75)
}
}
@@ -68,3 +65,18 @@
font-weight: $font-weight-semi-bold;
padding-bottom: 2px;
}
.alert-icon {
padding: 0 1rem 0 0;
display: flex;
align-items: center;
justify-content: center;
width: 2.5rem;
.fa {
font-size: 1.5rem;
}
}
.alert-body {
flex-grow: 1;
}
+5 -1
View File
@@ -28,7 +28,7 @@
background-color: $dropdownBackground !important;
color: $dropdownLinkColor !important;
border: 1px solid $dropdownBorder !important;
width: 320px !important;
width: 550px !important;
.ace_scroller {
.ace_selected, .ace_active-line, .ace_line-hover {
@@ -77,3 +77,7 @@ $doc-font-size: $font-size-sm;
.ace_tooltip {
border-radius: 3px;
}
.ace_hidden-cursors .ace_cursor {
opacity: 0 !important;
}
+1 -1
View File
@@ -5,7 +5,7 @@ $useDropShadow: false;
$attachmentOffset: 0%;
$easing: cubic-bezier(0, 0, 0.265, 1.00);
@include drop-theme("error", $errorBackground, $popover-color);
@include drop-theme("error", $popover-error-bg, $popover-color);
@include drop-theme("popover", $popover-bg, $popover-color, $popover-border-color);
@include drop-theme("help", $popover-help-bg, $popover-help-color);
+1 -1
View File
@@ -138,7 +138,7 @@ div.flot-text {
&--error {
display: block;
color: $text-color;
@include panel-corner-color($errorBackground);
@include panel-corner-color($popover-error-bg);
.fa:before {
content: "\f12a";
}
+1 -1
View File
@@ -176,7 +176,7 @@ define([
});
it('86399s 1 resolution', function() {
var range = { from: dateMath.parse('now-86399s'), to: dateMath.parse('now') };
var range = { from: dateMath.parse('now-86390s'), to: dateMath.parse('now') };
var res = kbn.calculateInterval(range, 1, null);
expect(res.interval).to.be('12h');
expect(res.intervalMs).to.be(43200000);
+6 -3
View File
@@ -31,11 +31,14 @@
<div class="page-alert-list">
<div ng-repeat='alert in dashAlerts.list' class="alert-{{alert.severity}} alert">
<div class="alert-icon"><i class="{{alert.icon}}"></i></div>
<div class="alert-body">
<div class="alert-title">{{alert.title}}</div>
<div class="alert-text" ng-bind='alert.text'></div>
</div>
<button type="button" class="alert-close" ng-click="dashAlerts.clear(alert)">
<i class="fa fa-times-circle"></i>
<i class="fa fa fa-remove"></i>
</button>
<div class="alert-title">{{alert.title}}</div>
<div ng-bind='alert.text'></div>
</div>
</div>