mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Merge remote-tracking branch 'upstream/master' into postgres-query-builder
This commit is contained in:
commit
1c0a188272
@ -28,7 +28,8 @@
|
||||
* **AuthProxy**: Support IPv6 in Auth proxy white list [#11330](https://github.com/grafana/grafana/pull/11330), thx [@corny](https://github.com/corny)
|
||||
* **SMTP**: Don't connect to STMP server using TLS unless configured. [#7189](https://github.com/grafana/grafana/issues/7189)
|
||||
* **Prometheus**: Escape backslash in labels correctly. [#10555](https://github.com/grafana/grafana/issues/10555), thx [@roidelapluie](https://github.com/roidelapluie)
|
||||
* **Variables** Case-insensitive sorting for template values [#11128](https://github.com/grafana/grafana/issues/11128) thx [@cross](https://github.com/cross)
|
||||
* **Variables**: Case-insensitive sorting for template values [#11128](https://github.com/grafana/grafana/issues/11128) thx [@cross](https://github.com/cross)
|
||||
* **Annotations (native)**: Change default limit from 10 to 100 when querying api [#11569](https://github.com/grafana/grafana/issues/11569), thx [@flopp999](https://github.com/flopp999)
|
||||
|
||||
# 5.0.4 (2018-03-28)
|
||||
|
||||
|
@ -7,7 +7,7 @@ clone_folder: c:\gopath\src\github.com\grafana\grafana
|
||||
environment:
|
||||
nodejs_version: "6"
|
||||
GOPATH: c:\gopath
|
||||
GOVERSION: 1.9.2
|
||||
GOVERSION: 1.10
|
||||
|
||||
install:
|
||||
- rmdir c:\go /s /q
|
||||
|
@ -138,6 +138,7 @@ datasources:
|
||||
```
|
||||
|
||||
#### Custom Settings per Datasource
|
||||
Please refer to each datasource documentation for specific provisioning examples.
|
||||
|
||||
| Datasource | Misc |
|
||||
| ---- | ---- |
|
||||
|
@ -43,6 +43,40 @@ server is running on AWS you can use IAM Roles and authentication will be handle
|
||||
|
||||
Checkout AWS docs on [IAM Roles](http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/iam-roles-for-amazon-ec2.html)
|
||||
|
||||
## IAM Policies
|
||||
|
||||
Grafana needs permissions granted via IAM to be able to read CloudWatch metrics
|
||||
and EC2 tags/instances. You can attach these permissions to IAM roles and
|
||||
utilize Grafana's built-in support for assuming roles.
|
||||
|
||||
Here is a minimal policy example:
|
||||
|
||||
```json
|
||||
{
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Sid": "AllowReadingMetricsFromCloudWatch",
|
||||
"Effect": "Allow",
|
||||
"Action": [
|
||||
"cloudwatch:ListMetrics",
|
||||
"cloudwatch:GetMetricStatistics"
|
||||
],
|
||||
"Resource": "*"
|
||||
},
|
||||
{
|
||||
"Sid": "AllowReadingTagsFromEC2",
|
||||
"Effect": "Allow",
|
||||
"Action": [
|
||||
"ec2:DescribeTags",
|
||||
"ec2:DescribeInstances"
|
||||
],
|
||||
"Resource": "*"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### AWS credentials file
|
||||
|
||||
Create a file at `~/.aws/credentials`. That is the `HOME` path for user running grafana-server.
|
||||
@ -173,3 +207,37 @@ Amazon provides 1 million CloudWatch API requests each month at no additional ch
|
||||
it costs $0.01 per 1,000 GetMetricStatistics or ListMetrics requests. For each query Grafana will
|
||||
issue a GetMetricStatistics request and every time you pick a dimension in the query editor
|
||||
Grafana will issue a ListMetrics request.
|
||||
|
||||
## Configure datasource with provisioning
|
||||
|
||||
It's now possible to configure datasources using config files with Grafanas provisioning system. You can read more about how it works and all the settings you can set for datasources on the [provisioning docs page](/administration/provisioning/#datasources)
|
||||
|
||||
Here are some provisioning examples for this datasource.
|
||||
|
||||
Using a credentials file
|
||||
```yaml
|
||||
apiVersion: 1
|
||||
|
||||
datasources:
|
||||
- name: Cloudwatch
|
||||
type: cloudwatch
|
||||
jsonData:
|
||||
authType: credentials
|
||||
defaultRegion: eu-west-2
|
||||
```
|
||||
|
||||
Using `accessKey` and `secretKey`
|
||||
|
||||
```yaml
|
||||
apiVersion: 1
|
||||
|
||||
datasources:
|
||||
- name: Cloudwatch
|
||||
type: cloudwatch
|
||||
jsonData:
|
||||
authType: keys
|
||||
defaultRegion: eu-west-2
|
||||
secureJsonData:
|
||||
accessKey: "<your access key>"
|
||||
secretKey: "<your secret key>"
|
||||
```
|
||||
|
@ -137,3 +137,23 @@ Query | You can leave the search query blank or specify a lucene query
|
||||
Time | The name of the time field, needs to be date field.
|
||||
Text | Event description field.
|
||||
Tags | Optional field name to use for event tags (can be an array or a CSV string).
|
||||
|
||||
## Configure datasource with provisioning
|
||||
|
||||
It's now possible to configure datasources using config files with Grafanas provisioning system. You can read more about how it works and all the settings you can set for datasources on the [provisioning docs page](/administration/provisioning/#datasources)
|
||||
|
||||
Here are some provisioning examples for this datasource.
|
||||
|
||||
```yaml
|
||||
apiVersion: 1
|
||||
|
||||
datasources:
|
||||
- name: Elastic
|
||||
type: elasticsearch
|
||||
access: proxy
|
||||
database: "[metrics-]YYYY.MM.DD"
|
||||
url: http://localhost:9200
|
||||
jsonData:
|
||||
interval: Daily
|
||||
timeField: "@timestamp"
|
||||
```
|
@ -120,3 +120,21 @@ queries via the Dashboard menu / Annotations view.
|
||||
|
||||
Graphite supports two ways to query annotations. A regular metric query, for this you use the `Graphite query` textbox. A Graphite events query, use the `Graphite event tags` textbox,
|
||||
specify a tag or wildcard (leave empty should also work)
|
||||
|
||||
## Configure datasource with provisioning
|
||||
|
||||
It's now possible to configure datasources using config files with Grafanas provisioning system. You can read more about how it works and all the settings you can set for datasources on the [provisioning docs page](/administration/provisioning/#datasources)
|
||||
|
||||
Here are some provisioning examples for this datasource.
|
||||
|
||||
```yaml
|
||||
apiVersion: 1
|
||||
|
||||
datasources:
|
||||
- name: Graphite
|
||||
type: graphite
|
||||
access: proxy
|
||||
url: http://localhost:8080
|
||||
jsonData:
|
||||
graphiteVersion: "1.1"
|
||||
```
|
||||
|
@ -174,3 +174,22 @@ SELECT title, description from events WHERE $timeFilter order asc
|
||||
For InfluxDB you need to enter a query like in the above example. You need to have the ```where $timeFilter```
|
||||
part. If you only select one column you will not need to enter anything in the column mapping fields. The
|
||||
Tags field can be a comma separated string.
|
||||
|
||||
## Configure datasource with provisioning
|
||||
|
||||
It's now possible to configure datasources using config files with Grafanas provisioning system. You can read more about how it works and all the settings you can set for datasources on the [provisioning docs page](/administration/provisioning/#datasources)
|
||||
|
||||
Here are some provisioning examples for this datasource.
|
||||
|
||||
```yaml
|
||||
apiVersion: 1
|
||||
|
||||
datasources:
|
||||
- name: InfluxDB
|
||||
type: influxdb
|
||||
access: proxy
|
||||
database: site
|
||||
user: grafana
|
||||
password: grafana
|
||||
url: http://localhost:8086
|
||||
```
|
@ -225,3 +225,21 @@ tags | Optional field name to use for event tags as a comma separated string.
|
||||
## Alerting
|
||||
|
||||
Time series queries should work in alerting conditions. Table formatted queries is not yet supported in alert rule conditions.
|
||||
|
||||
## Configure datasource with provisioning
|
||||
|
||||
It's now possible to configure datasources using config files with Grafanas provisioning system. You can read more about how it works and all the settings you can set for datasources on the [provisioning docs page](/administration/provisioning/#datasources)
|
||||
|
||||
Here are some provisioning examples for this datasource.
|
||||
|
||||
```yaml
|
||||
apiVersion: 1
|
||||
|
||||
datasources:
|
||||
- name: MySQL
|
||||
type: mysql
|
||||
url: localhost:3306
|
||||
database: grafana
|
||||
user: grafana
|
||||
password: password
|
||||
```
|
@ -88,3 +88,22 @@ Query | Description
|
||||
*tag_values(cpu, hostanme, env=$env, region=$region)* | Return tag values for cpu metric, selected env tag value, selected region tag value and tag key hostname
|
||||
|
||||
For details on OpenTSDB metric queries checkout the official [OpenTSDB documentation](http://opentsdb.net/docs/build/html/index.html)
|
||||
|
||||
## Configure datasource with provisioning
|
||||
|
||||
It's now possible to configure datasources using config files with Grafanas provisioning system. You can read more about how it works and all the settings you can set for datasources on the [provisioning docs page](/administration/provisioning/#datasources)
|
||||
|
||||
Here are some provisioning examples for this datasource.
|
||||
|
||||
```yaml
|
||||
apiVersion: 1
|
||||
|
||||
datasources:
|
||||
- name: OpenTsdb
|
||||
type: opentsdb
|
||||
access: proxy
|
||||
url: http://localhost:4242
|
||||
jsonData:
|
||||
tsdbResolution: 1
|
||||
tsdbVersion: 1
|
||||
```
|
@ -217,3 +217,25 @@ tags | Optional field name to use for event tags as a comma separated string.
|
||||
|
||||
Time series queries should work in alerting conditions. Table formatted queries is not yet supported in alert rule
|
||||
conditions.
|
||||
|
||||
## Configure datasource with provisioning
|
||||
|
||||
It's now possible to configure datasources using config files with Grafanas provisioning system. You can read more about how it works and all the settings you can set for datasources on the [provisioning docs page](/administration/provisioning/#datasources)
|
||||
|
||||
Here are some provisioning examples for this datasource.
|
||||
|
||||
```yaml
|
||||
apiVersion: 1
|
||||
|
||||
datasources:
|
||||
- name: Postgres
|
||||
type: postgres
|
||||
url: localhost:5432
|
||||
database: grafana
|
||||
user: grafana
|
||||
secureJsonData:
|
||||
password: "Password!"
|
||||
jsonData:
|
||||
sslmode: "disable" # disable/require/verify-ca/verify-full
|
||||
|
||||
```
|
@ -34,7 +34,7 @@ Name | Description
|
||||
*Basic Auth* | Enable basic authentication to the Prometheus data source.
|
||||
*User* | Name of your Prometheus user
|
||||
*Password* | Database user's password
|
||||
*Scrape interval* | This will be used as a lower limit for the Prometheus step query parameter. Default value is 15s.
|
||||
*Scrape interval* | This will be used as a lower limit for the Prometheus step query parameter. Default value is 15s.
|
||||
|
||||
## Query editor
|
||||
|
||||
@ -100,3 +100,19 @@ The step option is useful to limit the number of events returned from your query
|
||||
## Getting Grafana metrics into Prometheus
|
||||
|
||||
Since 4.6.0 Grafana exposes metrics for Prometheus on the `/metrics` endpoint. We also bundle a dashboard within Grafana so you can get started viewing your metrics faster. You can import the bundled dashboard by going to the data source edit page and click the dashboard tab. There you can find a dashboard for Grafana and one for Prometheus. Import and start viewing all the metrics!
|
||||
|
||||
## Configure datasource with provisioning
|
||||
|
||||
It's now possible to configure datasources using config files with Grafanas provisioning system. You can read more about how it works and all the settings you can set for datasources on the [provisioning docs page](/administration/provisioning/#datasources)
|
||||
|
||||
Here are some provisioning examples for this datasource.
|
||||
|
||||
```yaml
|
||||
apiVersion: 1
|
||||
|
||||
datasources:
|
||||
- name: Prometheus
|
||||
type: prometheus
|
||||
access: proxy
|
||||
url: http://localhost:9090
|
||||
```
|
@ -136,6 +136,7 @@
|
||||
"angular-route": "^1.6.6",
|
||||
"angular-sanitize": "^1.6.6",
|
||||
"babel-polyfill": "^6.26.0",
|
||||
"baron": "^3.0.3",
|
||||
"brace": "^0.10.0",
|
||||
"classnames": "^2.2.5",
|
||||
"clipboard": "^1.7.1",
|
||||
@ -151,7 +152,6 @@
|
||||
"moment": "^2.18.1",
|
||||
"mousetrap": "^1.6.0",
|
||||
"mousetrap-global-bind": "^1.1.0",
|
||||
"perfect-scrollbar": "^1.2.0",
|
||||
"prop-types": "^15.6.0",
|
||||
"react": "^16.2.0",
|
||||
"react-dom": "^16.2.0",
|
||||
|
@ -189,12 +189,6 @@ func (proxy *DataSourceProxy) getDirector() func(req *http.Request) {
|
||||
}
|
||||
|
||||
func (proxy *DataSourceProxy) validateRequest() error {
|
||||
if proxy.ds.Type == m.DS_INFLUXDB {
|
||||
if proxy.ctx.Query("db") != proxy.ds.Database {
|
||||
return errors.New("Datasource is not configured to allow this database")
|
||||
}
|
||||
}
|
||||
|
||||
if !checkWhiteList(proxy.ctx, proxy.targetUrl.Host) {
|
||||
return errors.New("Target url is not a valid target")
|
||||
}
|
||||
|
@ -72,7 +72,10 @@ func (this *DingDingNotifier) Notify(evalContext *alerting.EvalContext) error {
|
||||
this.log.Error("Failed to create Json data", "error", err, "dingding", this.Name)
|
||||
}
|
||||
|
||||
body, _ := bodyJSON.MarshalJSON()
|
||||
body, err := bodyJSON.MarshalJSON()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cmd := &m.SendWebhookSync{
|
||||
Url: this.Url,
|
||||
|
@ -202,7 +202,7 @@ func (r *SqlAnnotationRepo) Find(query *annotations.ItemQuery) ([]*annotations.I
|
||||
}
|
||||
|
||||
if query.Limit == 0 {
|
||||
query.Limit = 10
|
||||
query.Limit = 100
|
||||
}
|
||||
|
||||
sql.WriteString(fmt.Sprintf(" ORDER BY epoch DESC LIMIT %v", query.Limit))
|
||||
|
@ -1,5 +1,5 @@
|
||||
import React from 'react';
|
||||
import PerfectScrollbar from 'perfect-scrollbar';
|
||||
import baron from 'baron';
|
||||
|
||||
export interface Props {
|
||||
children: any;
|
||||
@ -8,31 +8,36 @@ export interface Props {
|
||||
|
||||
export default class ScrollBar extends React.Component<Props, any> {
|
||||
private container: any;
|
||||
private ps: PerfectScrollbar;
|
||||
private scrollbar: baron;
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.ps = new PerfectScrollbar(this.container, {
|
||||
wheelPropagation: true,
|
||||
this.scrollbar = baron({
|
||||
root: this.container.parentElement,
|
||||
scroller: this.container,
|
||||
bar: '.baron__bar',
|
||||
barOnCls: '_scrollbar',
|
||||
scrollingCls: '_scrolling',
|
||||
track: '.baron__track',
|
||||
});
|
||||
}
|
||||
|
||||
componentDidUpdate() {
|
||||
this.ps.update();
|
||||
this.scrollbar.update();
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
this.ps.destroy();
|
||||
this.scrollbar.dispose();
|
||||
}
|
||||
|
||||
// methods can be invoked by outside
|
||||
setScrollTop(top) {
|
||||
if (this.container) {
|
||||
this.container.scrollTop = top;
|
||||
this.ps.update();
|
||||
this.scrollbar.update();
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -42,7 +47,7 @@ export default class ScrollBar extends React.Component<Props, any> {
|
||||
setScrollLeft(left) {
|
||||
if (this.container) {
|
||||
this.container.scrollLeft = left;
|
||||
this.ps.update();
|
||||
this.scrollbar.update();
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -55,8 +60,14 @@ export default class ScrollBar extends React.Component<Props, any> {
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div className={this.props.className} ref={this.handleRef}>
|
||||
{this.props.children}
|
||||
<div className="baron baron__root baron__clipper">
|
||||
<div className={this.props.className + ' baron__scroller'} ref={this.handleRef}>
|
||||
{this.props.children}
|
||||
</div>
|
||||
|
||||
<div className="baron__track">
|
||||
<div className="baron__bar" />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -167,6 +167,7 @@ export function grafanaAppDirective(playlistSrv, contextSrv, $timeout, $rootScop
|
||||
if (sidemenuHidden) {
|
||||
sidemenuHidden = false;
|
||||
body.addClass('sidemenu-open');
|
||||
appEvents.emit('toggle-inactive-mode');
|
||||
$timeout(function() {
|
||||
$rootScope.$broadcast('render');
|
||||
}, 100);
|
||||
|
41
public/app/core/components/scroll/page_scroll.ts
Normal file
41
public/app/core/components/scroll/page_scroll.ts
Normal file
@ -0,0 +1,41 @@
|
||||
import coreModule from 'app/core/core_module';
|
||||
import appEvents from 'app/core/app_events';
|
||||
|
||||
export function pageScrollbar() {
|
||||
return {
|
||||
restrict: 'A',
|
||||
link: function(scope, elem, attrs) {
|
||||
let lastPos = 0;
|
||||
|
||||
appEvents.on(
|
||||
'dash-scroll',
|
||||
evt => {
|
||||
if (evt.restore) {
|
||||
elem[0].scrollTop = lastPos;
|
||||
return;
|
||||
}
|
||||
|
||||
lastPos = elem[0].scrollTop;
|
||||
|
||||
if (evt.animate) {
|
||||
elem.animate({ scrollTop: evt.pos }, 500);
|
||||
} else {
|
||||
elem[0].scrollTop = evt.pos;
|
||||
}
|
||||
},
|
||||
scope
|
||||
);
|
||||
|
||||
scope.$on('$routeChangeSuccess', () => {
|
||||
lastPos = 0;
|
||||
elem[0].scrollTop = 0;
|
||||
elem[0].focus();
|
||||
});
|
||||
|
||||
elem[0].tabIndex = -1;
|
||||
elem[0].focus();
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
coreModule.directive('pageScrollbar', pageScrollbar);
|
@ -1,15 +1,44 @@
|
||||
import PerfectScrollbar from 'perfect-scrollbar';
|
||||
import $ from 'jquery';
|
||||
import baron from 'baron';
|
||||
import coreModule from 'app/core/core_module';
|
||||
import appEvents from 'app/core/app_events';
|
||||
|
||||
const scrollBarHTML = `
|
||||
<div class="baron__track">
|
||||
<div class="baron__bar"></div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
const scrollRootClass = 'baron baron__root';
|
||||
const scrollerClass = 'baron__scroller';
|
||||
|
||||
export function geminiScrollbar() {
|
||||
return {
|
||||
restrict: 'A',
|
||||
link: function(scope, elem, attrs) {
|
||||
let scrollbar = new PerfectScrollbar(elem[0], {
|
||||
wheelPropagation: true,
|
||||
wheelSpeed: 3,
|
||||
});
|
||||
let scrollRoot = elem.parent();
|
||||
let scroller = elem;
|
||||
|
||||
if (attrs.grafanaScrollbar && attrs.grafanaScrollbar === 'scrollonroot') {
|
||||
scrollRoot = scroller;
|
||||
}
|
||||
|
||||
scrollRoot.addClass(scrollRootClass);
|
||||
$(scrollBarHTML).appendTo(scrollRoot);
|
||||
elem.addClass(scrollerClass);
|
||||
|
||||
let scrollParams = {
|
||||
root: scrollRoot[0],
|
||||
scroller: scroller[0],
|
||||
bar: '.baron__bar',
|
||||
barOnCls: '_scrollbar',
|
||||
scrollingCls: '_scrolling',
|
||||
track: '.baron__track',
|
||||
direction: 'v',
|
||||
};
|
||||
|
||||
let scrollbar = baron(scrollParams);
|
||||
|
||||
let lastPos = 0;
|
||||
|
||||
appEvents.on(
|
||||
@ -31,13 +60,24 @@ export function geminiScrollbar() {
|
||||
scope
|
||||
);
|
||||
|
||||
// force updating dashboard width
|
||||
appEvents.on('toggle-sidemenu', forceUpdate, scope);
|
||||
appEvents.on('toggle-sidemenu-hidden', forceUpdate, scope);
|
||||
appEvents.on('toggle-view-mode', forceUpdate, scope);
|
||||
appEvents.on('toggle-kiosk-mode', forceUpdate, scope);
|
||||
appEvents.on('toggle-inactive-mode', forceUpdate, scope);
|
||||
|
||||
function forceUpdate() {
|
||||
scrollbar.scroll();
|
||||
}
|
||||
|
||||
scope.$on('$routeChangeSuccess', () => {
|
||||
lastPos = 0;
|
||||
elem[0].scrollTop = 0;
|
||||
});
|
||||
|
||||
scope.$on('$destroy', () => {
|
||||
scrollbar.destroy();
|
||||
scrollbar.dispose();
|
||||
});
|
||||
},
|
||||
};
|
||||
|
@ -19,6 +19,7 @@
|
||||
|
||||
<div class="search-dropdown">
|
||||
<div class="search-dropdown__col_1">
|
||||
<div class="search-results-scroller">
|
||||
<div class="search-results-container" grafana-scrollbar>
|
||||
<h6 ng-show="!ctrl.isLoading && ctrl.results.length === 0">No dashboards matching your query were found.</h6>
|
||||
<dashboard-search-results
|
||||
@ -27,6 +28,7 @@
|
||||
on-folder-expanding="ctrl.folderExpanding()"
|
||||
on-folder-expanded="ctrl.folderExpanded($folder)" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="search-dropdown__col_2">
|
||||
|
@ -47,6 +47,7 @@ import { NavModelSrv, NavModel } from './nav_model_srv';
|
||||
import { userPicker } from './components/user_picker';
|
||||
import { teamPicker } from './components/team_picker';
|
||||
import { geminiScrollbar } from './components/scroll/scroll';
|
||||
import { pageScrollbar } from './components/scroll/page_scroll';
|
||||
import { gfPageDirective } from './components/gf_page';
|
||||
import { orgSwitcher } from './components/org_switcher';
|
||||
import { profiler } from './profiler';
|
||||
@ -85,6 +86,7 @@ export {
|
||||
userPicker,
|
||||
teamPicker,
|
||||
geminiScrollbar,
|
||||
pageScrollbar,
|
||||
gfPageDirective,
|
||||
orgSwitcher,
|
||||
manageDashboardsDirective,
|
||||
|
@ -103,7 +103,7 @@ export class AddPanelPanel extends React.Component<AddPanelPanelProps, AddPanelP
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div className="panel-container">
|
||||
<div className="panel-container add-panel-container">
|
||||
<div className="add-panel">
|
||||
<div className="add-panel__header">
|
||||
<i className="gicon gicon-add-panel" />
|
||||
|
@ -196,9 +196,10 @@ export class DashboardViewState {
|
||||
this.oldTimeRange = ctrl.range;
|
||||
this.fullscreenPanel = panelScope;
|
||||
|
||||
// Firefox doesn't return scrollTop postion properly if 'dash-scroll' is emitted after setViewMode()
|
||||
this.$scope.appEvent('dash-scroll', { animate: false, pos: 0 });
|
||||
this.dashboard.setViewMode(ctrl.panel, true, ctrl.editMode);
|
||||
this.$scope.appEvent('panel-fullscreen-enter', { panelId: ctrl.panel.id });
|
||||
this.$scope.appEvent('dash-scroll', { animate: false, pos: 0 });
|
||||
}
|
||||
|
||||
registerPanel(panelScope) {
|
||||
|
@ -1,6 +1,7 @@
|
||||
import angular from 'angular';
|
||||
import $ from 'jquery';
|
||||
import Drop from 'tether-drop';
|
||||
import PerfectScrollbar from 'perfect-scrollbar';
|
||||
import baron from 'baron';
|
||||
|
||||
var module = angular.module('grafana.directives');
|
||||
|
||||
@ -86,6 +87,9 @@ module.directive('grafanaPanel', function($rootScope, $document, $timeout) {
|
||||
|
||||
function panelHeightUpdated() {
|
||||
panelContent.css({ height: ctrl.height + 'px' });
|
||||
}
|
||||
|
||||
function resizeScrollableContent() {
|
||||
if (panelScrollbar) {
|
||||
panelScrollbar.update();
|
||||
}
|
||||
@ -100,9 +104,30 @@ module.directive('grafanaPanel', function($rootScope, $document, $timeout) {
|
||||
// update scrollbar after mounting
|
||||
ctrl.events.on('component-did-mount', () => {
|
||||
if (ctrl.__proto__.constructor.scrollable) {
|
||||
panelScrollbar = new PerfectScrollbar(panelContent[0], {
|
||||
wheelPropagation: true,
|
||||
const scrollRootClass = 'baron baron__root baron__clipper panel-content--scrollable';
|
||||
const scrollerClass = 'baron__scroller';
|
||||
const scrollBarHTML = `
|
||||
<div class="baron__track">
|
||||
<div class="baron__bar"></div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
let scrollRoot = panelContent;
|
||||
let scroller = panelContent.find(':first').find(':first');
|
||||
|
||||
scrollRoot.addClass(scrollRootClass);
|
||||
$(scrollBarHTML).appendTo(scrollRoot);
|
||||
scroller.addClass(scrollerClass);
|
||||
|
||||
panelScrollbar = baron({
|
||||
root: scrollRoot[0],
|
||||
scroller: scroller[0],
|
||||
bar: '.baron__bar',
|
||||
barOnCls: '_scrollbar',
|
||||
scrollingCls: '_scrolling',
|
||||
});
|
||||
|
||||
panelScrollbar.scroll();
|
||||
}
|
||||
});
|
||||
|
||||
@ -110,6 +135,7 @@ module.directive('grafanaPanel', function($rootScope, $document, $timeout) {
|
||||
ctrl.calculatePanelHeight();
|
||||
panelHeightUpdated();
|
||||
$timeout(() => {
|
||||
resizeScrollableContent();
|
||||
ctrl.render();
|
||||
});
|
||||
});
|
||||
@ -199,7 +225,7 @@ module.directive('grafanaPanel', function($rootScope, $document, $timeout) {
|
||||
}
|
||||
|
||||
if (panelScrollbar) {
|
||||
panelScrollbar.update();
|
||||
panelScrollbar.dispose();
|
||||
}
|
||||
});
|
||||
},
|
||||
|
@ -198,7 +198,9 @@ export class QueryVariable implements Variable {
|
||||
}
|
||||
});
|
||||
} else if (sortType === 3) {
|
||||
options = _.sortBy(options, opt => { return _.toLower(opt.text); });
|
||||
options = _.sortBy(options, opt => {
|
||||
return _.toLower(opt.text);
|
||||
});
|
||||
}
|
||||
|
||||
if (reverseSort) {
|
||||
|
@ -1,18 +1,18 @@
|
||||
<div dash-class ng-if="ctrl.dashboard">
|
||||
<dashnav dashboard="ctrl.dashboard"></dashnav>
|
||||
|
||||
<div class="scroll-canvas scroll-canvas--dashboard" grafana-scrollbar>
|
||||
<dashboard-settings dashboard="ctrl.dashboard"
|
||||
ng-if="ctrl.dashboardViewState.state.editview"
|
||||
class="dashboard-settings">
|
||||
</dashboard-settings>
|
||||
<div class="scroll-canvas scroll-canvas--dashboard" page-scrollbar>
|
||||
<dashboard-settings dashboard="ctrl.dashboard"
|
||||
ng-if="ctrl.dashboardViewState.state.editview"
|
||||
class="dashboard-settings">
|
||||
</dashboard-settings>
|
||||
|
||||
<div class="dashboard-container">
|
||||
<dashboard-submenu ng-if="ctrl.dashboard.meta.submenuEnabled" dashboard="ctrl.dashboard">
|
||||
</dashboard-submenu>
|
||||
<div class="dashboard-container">
|
||||
<dashboard-submenu ng-if="ctrl.dashboard.meta.submenuEnabled" dashboard="ctrl.dashboard">
|
||||
</dashboard-submenu>
|
||||
|
||||
<dashboard-grid get-panel-container="ctrl.getPanelContainer">
|
||||
</dashboard-grid>
|
||||
</div>
|
||||
</div>
|
||||
<dashboard-grid get-panel-container="ctrl.getPanelContainer">
|
||||
</dashboard-grid>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -82,7 +82,7 @@ export default class InfluxDatasource {
|
||||
// replace templated variables
|
||||
allQueries = this.templateSrv.replace(allQueries, scopedVars);
|
||||
|
||||
return this._seriesQuery(allQueries).then((data): any => {
|
||||
return this._seriesQuery(allQueries, options).then((data): any => {
|
||||
if (!data || !data.results) {
|
||||
return [];
|
||||
}
|
||||
@ -135,7 +135,7 @@ export default class InfluxDatasource {
|
||||
var query = options.annotation.query.replace('$timeFilter', timeFilter);
|
||||
query = this.templateSrv.replace(query, null, 'regex');
|
||||
|
||||
return this._seriesQuery(query).then(data => {
|
||||
return this._seriesQuery(query, options).then(data => {
|
||||
if (!data || !data.results || !data.results[0]) {
|
||||
throw { message: 'No results in response from InfluxDB' };
|
||||
}
|
||||
@ -164,30 +164,30 @@ export default class InfluxDatasource {
|
||||
return false;
|
||||
}
|
||||
|
||||
metricFindQuery(query) {
|
||||
metricFindQuery(query: string, options?: any) {
|
||||
var interpolated = this.templateSrv.replace(query, null, 'regex');
|
||||
|
||||
return this._seriesQuery(interpolated).then(_.curry(this.responseParser.parse)(query));
|
||||
return this._seriesQuery(interpolated, options).then(_.curry(this.responseParser.parse)(query));
|
||||
}
|
||||
|
||||
getTagKeys(options) {
|
||||
var queryBuilder = new InfluxQueryBuilder({ measurement: '', tags: [] }, this.database);
|
||||
var query = queryBuilder.buildExploreQuery('TAG_KEYS');
|
||||
return this.metricFindQuery(query);
|
||||
return this.metricFindQuery(query, options);
|
||||
}
|
||||
|
||||
getTagValues(options) {
|
||||
var queryBuilder = new InfluxQueryBuilder({ measurement: '', tags: [] }, this.database);
|
||||
var query = queryBuilder.buildExploreQuery('TAG_VALUES', options.key);
|
||||
return this.metricFindQuery(query);
|
||||
return this.metricFindQuery(query, options);
|
||||
}
|
||||
|
||||
_seriesQuery(query) {
|
||||
_seriesQuery(query: string, options?: any) {
|
||||
if (!query) {
|
||||
return this.$q.when({ results: [] });
|
||||
}
|
||||
|
||||
return this._influxRequest('GET', '/query', { q: query, epoch: 'ms' });
|
||||
return this._influxRequest('GET', '/query', { q: query, epoch: 'ms' }, options);
|
||||
}
|
||||
|
||||
serializeParams(params) {
|
||||
@ -225,21 +225,21 @@ export default class InfluxDatasource {
|
||||
});
|
||||
}
|
||||
|
||||
_influxRequest(method, url, data) {
|
||||
var self = this;
|
||||
_influxRequest(method: string, url: string, data: any, options?: any) {
|
||||
const currentUrl = this.urls.shift();
|
||||
this.urls.push(currentUrl);
|
||||
|
||||
var currentUrl = self.urls.shift();
|
||||
self.urls.push(currentUrl);
|
||||
let params: any = {};
|
||||
|
||||
var params: any = {};
|
||||
|
||||
if (self.username) {
|
||||
params.u = self.username;
|
||||
params.p = self.password;
|
||||
if (this.username) {
|
||||
params.u = this.username;
|
||||
params.p = this.password;
|
||||
}
|
||||
|
||||
if (self.database) {
|
||||
params.db = self.database;
|
||||
if (options && options.database) {
|
||||
params.db = options.database;
|
||||
} else if (this.database) {
|
||||
params.db = this.database;
|
||||
}
|
||||
|
||||
if (method === 'GET') {
|
||||
@ -247,7 +247,7 @@ export default class InfluxDatasource {
|
||||
data = null;
|
||||
}
|
||||
|
||||
var options: any = {
|
||||
let req: any = {
|
||||
method: method,
|
||||
url: currentUrl + url,
|
||||
params: params,
|
||||
@ -257,15 +257,15 @@ export default class InfluxDatasource {
|
||||
paramSerializer: this.serializeParams,
|
||||
};
|
||||
|
||||
options.headers = options.headers || {};
|
||||
req.headers = req.headers || {};
|
||||
if (this.basicAuth || this.withCredentials) {
|
||||
options.withCredentials = true;
|
||||
req.withCredentials = true;
|
||||
}
|
||||
if (self.basicAuth) {
|
||||
options.headers.Authorization = self.basicAuth;
|
||||
if (this.basicAuth) {
|
||||
req.headers.Authorization = this.basicAuth;
|
||||
}
|
||||
|
||||
return this.backendSrv.datasourceRequest(options).then(
|
||||
return this.backendSrv.datasourceRequest(req).then(
|
||||
result => {
|
||||
return result.data;
|
||||
},
|
||||
|
@ -23,6 +23,20 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="gf-form-group">
|
||||
<div class="grafana-info-box">
|
||||
<h5>Database Access</h5>
|
||||
<p>
|
||||
Setting the database for this datasource does not deny access to other databases. The InfluxDB query syntax allows
|
||||
switching the database in the query. For example:
|
||||
<code>SHOW MEASUREMENTS ON _internal</code> or <code>SELECT * FROM "_internal".."database" LIMIT 10</code>
|
||||
<br/><br/>
|
||||
To support data isolation and security, make sure appropriate permissions are configured in InfluxDB.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="gf-form-group">
|
||||
<div class="gf-form-inline">
|
||||
<div class="gf-form">
|
||||
|
@ -1,17 +1,19 @@
|
||||
<div class="dashlist" ng-repeat="group in ctrl.groups">
|
||||
<div class="dashlist-section" ng-if="group.show">
|
||||
<h6 class="dashlist-section-header" ng-show="ctrl.panel.headings">
|
||||
{{group.header}}
|
||||
</h6>
|
||||
<div class="dashlist-item" ng-repeat="dash in group.list">
|
||||
<a class="dashlist-link dashlist-link-{{dash.type}}" href="{{dash.url}}">
|
||||
<span class="dashlist-title">
|
||||
{{dash.title}}
|
||||
</span>
|
||||
<span class="dashlist-star" ng-click="ctrl.starDashboard(dash, $event)">
|
||||
<i class="fa" ng-class="{'fa-star': dash.isStarred, 'fa-star-o': dash.isStarred === false}"></i>
|
||||
</span>
|
||||
</a>
|
||||
<div>
|
||||
<div class="dashlist" ng-repeat="group in ctrl.groups">
|
||||
<div class="dashlist-section" ng-if="group.show">
|
||||
<h6 class="dashlist-section-header" ng-show="ctrl.panel.headings">
|
||||
{{group.header}}
|
||||
</h6>
|
||||
<div class="dashlist-item" ng-repeat="dash in group.list">
|
||||
<a class="dashlist-link dashlist-link-{{dash.type}}" href="{{dash.url}}">
|
||||
<span class="dashlist-title">
|
||||
{{dash.title}}
|
||||
</span>
|
||||
<span class="dashlist-star" ng-click="ctrl.starDashboard(dash, $event)">
|
||||
<i class="fa" ng-class="{'fa-star': dash.isStarred, 'fa-star-o': dash.isStarred === false}"></i>
|
||||
</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -1,7 +1,7 @@
|
||||
import angular from 'angular';
|
||||
import _ from 'lodash';
|
||||
import $ from 'jquery';
|
||||
import PerfectScrollbar from 'perfect-scrollbar';
|
||||
import baron from 'baron';
|
||||
|
||||
var module = angular.module('grafana.directives');
|
||||
|
||||
@ -16,11 +16,10 @@ module.directive('graphLegend', function(popoverSrv, $timeout) {
|
||||
var i;
|
||||
var legendScrollbar;
|
||||
const legendRightDefaultWidth = 10;
|
||||
let legendElem = elem.parent();
|
||||
|
||||
scope.$on('$destroy', function() {
|
||||
if (legendScrollbar) {
|
||||
legendScrollbar.destroy();
|
||||
}
|
||||
destroyScrollbar();
|
||||
});
|
||||
|
||||
ctrl.events.on('render-legend', () => {
|
||||
@ -112,7 +111,7 @@ module.directive('graphLegend', function(popoverSrv, $timeout) {
|
||||
}
|
||||
|
||||
function render() {
|
||||
let legendWidth = elem.width();
|
||||
let legendWidth = legendElem.width();
|
||||
if (!ctrl.panel.legend.show) {
|
||||
elem.empty();
|
||||
firstRender = true;
|
||||
@ -134,8 +133,8 @@ module.directive('graphLegend', function(popoverSrv, $timeout) {
|
||||
// Set width so it works with IE11
|
||||
var width: any = panel.legend.rightSide && panel.legend.sideWidth ? panel.legend.sideWidth + 'px' : '';
|
||||
var ieWidth: any = panel.legend.rightSide && panel.legend.sideWidth ? panel.legend.sideWidth - 1 + 'px' : '';
|
||||
elem.css('min-width', width);
|
||||
elem.css('width', ieWidth);
|
||||
legendElem.css('min-width', width);
|
||||
legendElem.css('width', ieWidth);
|
||||
|
||||
elem.toggleClass('graph-legend-table', panel.legend.alignAsTable === true);
|
||||
|
||||
@ -241,8 +240,10 @@ module.directive('graphLegend', function(popoverSrv, $timeout) {
|
||||
tbodyElem.append(tableHeaderElem);
|
||||
tbodyElem.append(seriesElements);
|
||||
elem.append(tbodyElem);
|
||||
tbodyElem.wrap('<div class="graph-legend-scroll"></div>');
|
||||
} else {
|
||||
elem.append(seriesElements);
|
||||
elem.append('<div class="graph-legend-scroll"></div>');
|
||||
elem.find('.graph-legend-scroll').append(seriesElements);
|
||||
}
|
||||
|
||||
if (!panel.legend.rightSide || (panel.legend.rightSide && legendWidth !== legendRightDefaultWidth)) {
|
||||
@ -253,23 +254,45 @@ module.directive('graphLegend', function(popoverSrv, $timeout) {
|
||||
}
|
||||
|
||||
function addScrollbar() {
|
||||
const scrollbarOptions = {
|
||||
// Number of pixels the content height can surpass the container height without enabling the scroll bar.
|
||||
scrollYMarginOffset: 2,
|
||||
suppressScrollX: true,
|
||||
wheelPropagation: true,
|
||||
const scrollRootClass = 'baron baron__root';
|
||||
const scrollerClass = 'baron__scroller';
|
||||
const scrollBarHTML = `
|
||||
<div class="baron__track">
|
||||
<div class="baron__bar"></div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
let scrollRoot = elem;
|
||||
let scroller = elem.find('.graph-legend-scroll');
|
||||
|
||||
// clear existing scroll bar track to prevent duplication
|
||||
scrollRoot.find('.baron__track').remove();
|
||||
|
||||
scrollRoot.addClass(scrollRootClass);
|
||||
$(scrollBarHTML).appendTo(scrollRoot);
|
||||
scroller.addClass(scrollerClass);
|
||||
|
||||
let scrollbarParams = {
|
||||
root: scrollRoot[0],
|
||||
scroller: scroller[0],
|
||||
bar: '.baron__bar',
|
||||
track: '.baron__track',
|
||||
barOnCls: '_scrollbar',
|
||||
scrollingCls: '_scrolling',
|
||||
};
|
||||
|
||||
if (!legendScrollbar) {
|
||||
legendScrollbar = new PerfectScrollbar(elem[0], scrollbarOptions);
|
||||
legendScrollbar = baron(scrollbarParams);
|
||||
} else {
|
||||
legendScrollbar.update();
|
||||
destroyScrollbar();
|
||||
legendScrollbar = baron(scrollbarParams);
|
||||
}
|
||||
legendScrollbar.scroll();
|
||||
}
|
||||
|
||||
function destroyScrollbar() {
|
||||
if (legendScrollbar) {
|
||||
legendScrollbar.destroy();
|
||||
legendScrollbar.dispose();
|
||||
legendScrollbar = undefined;
|
||||
}
|
||||
}
|
||||
|
@ -3,7 +3,9 @@ var template = `
|
||||
<div class="graph-panel__chart" grafana-graph ng-dblclick="ctrl.zoomOut()">
|
||||
</div>
|
||||
|
||||
<div class="graph-legend" graph-legend></div>
|
||||
<div class="graph-legend">
|
||||
<div class="graph-legend-content" graph-legend></div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
|
@ -1,5 +1,13 @@
|
||||
.add-panel-container {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.add-panel {
|
||||
height: 100%;
|
||||
|
||||
.baron__root {
|
||||
height: calc(100% - 43px);
|
||||
}
|
||||
}
|
||||
|
||||
.add-panel__header {
|
||||
@ -39,7 +47,6 @@
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap;
|
||||
overflow: auto;
|
||||
height: calc(100% - 43px);
|
||||
align-content: flex-start;
|
||||
justify-content: space-around;
|
||||
position: relative;
|
||||
|
@ -49,6 +49,7 @@
|
||||
}
|
||||
|
||||
.graph-legend {
|
||||
display: flex;
|
||||
flex: 0 1 auto;
|
||||
max-height: 30%;
|
||||
margin: 0;
|
||||
@ -56,11 +57,27 @@
|
||||
padding-top: 6px;
|
||||
position: relative;
|
||||
|
||||
// fix for Firefox (white stripe on the right of scrollbar)
|
||||
width: calc(100% - 1px);
|
||||
|
||||
.popover-content {
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.graph-legend-content {
|
||||
position: relative;
|
||||
|
||||
// fix for Firefox (white stripe on the right of scrollbar)
|
||||
width: calc(100% - 1px);
|
||||
}
|
||||
|
||||
.graph-legend-scroll {
|
||||
position: relative;
|
||||
overflow: auto !important;
|
||||
padding: 1px;
|
||||
}
|
||||
|
||||
.graph-legend-icon {
|
||||
position: relative;
|
||||
padding-right: 4px;
|
||||
@ -115,8 +132,20 @@
|
||||
// fix for phantomjs
|
||||
.body--phantomjs {
|
||||
.graph-panel--legend-right {
|
||||
.graph-legend {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.graph-panel__chart {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.graph-legend-table {
|
||||
display: table;
|
||||
|
||||
.graph-legend-scroll {
|
||||
display: table;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -124,9 +153,9 @@
|
||||
.graph-legend-table {
|
||||
tbody {
|
||||
display: block;
|
||||
position: relative;
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
height: 100%;
|
||||
padding-bottom: 1px;
|
||||
padding-right: 5px;
|
||||
padding-left: 5px;
|
||||
|
@ -9,6 +9,11 @@
|
||||
-ms-touch-action: auto;
|
||||
}
|
||||
|
||||
// ._scrollbar {
|
||||
// overflow-x: hidden !important;
|
||||
// overflow-y: auto;
|
||||
// }
|
||||
|
||||
/*
|
||||
* Scrollbar rail styles
|
||||
*/
|
||||
@ -101,7 +106,7 @@
|
||||
opacity: 0.9;
|
||||
}
|
||||
|
||||
// Srollbars
|
||||
// Scrollbars
|
||||
//
|
||||
|
||||
::-webkit-scrollbar {
|
||||
@ -172,3 +177,120 @@
|
||||
border-top: 1px solid $scrollbarBorder;
|
||||
border-left: 1px solid $scrollbarBorder;
|
||||
}
|
||||
|
||||
// Baron styles
|
||||
|
||||
.baron {
|
||||
// display: inline-block; // this brakes phantomjs rendering (width becomes 0)
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
// Fix for side menu on mobile devices
|
||||
.main-view.baron {
|
||||
width: unset;
|
||||
}
|
||||
|
||||
.baron__clipper {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.baron__scroller {
|
||||
overflow-y: scroll;
|
||||
-ms-overflow-style: none;
|
||||
-moz-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
margin: 0;
|
||||
border: 0;
|
||||
padding: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
/* remove line to customize scrollbar in iOs */
|
||||
}
|
||||
|
||||
.baron__scroller::-webkit-scrollbar {
|
||||
width: 0;
|
||||
height: 0;
|
||||
}
|
||||
|
||||
.baron__track {
|
||||
display: none;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
}
|
||||
|
||||
.baron._scrollbar .baron__track {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.baron__free {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
}
|
||||
|
||||
.baron__bar {
|
||||
display: none;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
z-index: 1;
|
||||
// width: 10px;
|
||||
background: #999;
|
||||
|
||||
// height: 15px;
|
||||
width: 15px;
|
||||
transition: background-color 0.2s linear, opacity 0.2s linear;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.baron._scrollbar .baron__bar {
|
||||
display: block;
|
||||
|
||||
@include gradient-vertical($scrollbarBackground, $scrollbarBackground2);
|
||||
border-radius: 6px;
|
||||
width: 6px;
|
||||
/* there must be 'right' for ps__thumb-y */
|
||||
right: 0px;
|
||||
/* please don't change 'position' */
|
||||
position: absolute;
|
||||
|
||||
// background-color: transparent;
|
||||
// opacity: 0.6;
|
||||
|
||||
&:hover,
|
||||
&:focus {
|
||||
// background-color: transparent;
|
||||
opacity: 0.9;
|
||||
}
|
||||
}
|
||||
|
||||
.panel-hover-highlight .baron__track .baron__bar {
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
.baron._scrolling > .baron__track .baron__bar {
|
||||
opacity: 0.9;
|
||||
}
|
||||
|
||||
// fix for phantomjs
|
||||
.body--phantomjs .baron__track .baron__bar {
|
||||
opacity: 0 !important;
|
||||
}
|
||||
|
||||
.baron__control {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.baron.panel-content--scrollable {
|
||||
// Width needs to be set to prevent content width issues
|
||||
// Set to less than 100% for fixing Firefox issue (white stripe on the right of scrollbar)
|
||||
width: calc(100% - 2px);
|
||||
|
||||
.baron__scroller {
|
||||
padding-top: 1px;
|
||||
}
|
||||
}
|
||||
|
@ -102,14 +102,21 @@
|
||||
}
|
||||
}
|
||||
|
||||
.search-results-scroller {
|
||||
display: flex;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.search-results-container {
|
||||
height: 100%;
|
||||
display: block;
|
||||
padding: $spacer;
|
||||
position: relative;
|
||||
flex-grow: 10;
|
||||
margin-bottom: 1rem;
|
||||
|
||||
// Fix for search scroller in mobile view
|
||||
height: unset;
|
||||
|
||||
.label-tag {
|
||||
margin-left: 6px;
|
||||
font-size: 11px;
|
||||
|
@ -28,12 +28,20 @@
|
||||
width: 100%;
|
||||
overflow: auto;
|
||||
height: 100%;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
|
||||
&--dashboard {
|
||||
height: calc(100% - 56px);
|
||||
}
|
||||
}
|
||||
|
||||
// fix for phantomjs
|
||||
.body--phantomjs {
|
||||
.scroll-canvas {
|
||||
overflow: hidden;
|
||||
}
|
||||
}
|
||||
|
||||
.page-body {
|
||||
padding-top: $spacer*2;
|
||||
min-height: 500px;
|
||||
|
@ -16,7 +16,7 @@
|
||||
<link rel="icon" type="image/png" href="public/img/fav32.png">
|
||||
<link rel="mask-icon" href="public/img/grafana_mask_icon.svg" color="#F05A28">
|
||||
<link rel="apple-touch-icon" href="public/img/fav32.png">
|
||||
|
||||
|
||||
</head>
|
||||
|
||||
<body ng-cloak class="theme-[[ .Theme ]]">
|
||||
@ -40,7 +40,7 @@
|
||||
</div>
|
||||
|
||||
<div class="main-view">
|
||||
<div class="scroll-canvas" grafana-scrollbar>
|
||||
<div class="scroll-canvas" page-scrollbar>
|
||||
<div ng-view></div>
|
||||
|
||||
<footer class="footer">
|
||||
|
@ -20,17 +20,4 @@ echo "building backend with install to cache pkgs"
|
||||
exit_if_fail time go install ./pkg/cmd/grafana-server
|
||||
|
||||
echo "running go test"
|
||||
|
||||
set -e
|
||||
echo "" > coverage.txt
|
||||
|
||||
time for d in $(go list ./pkg/...); do
|
||||
exit_if_fail go test -coverprofile=profile.out -covermode=atomic $d
|
||||
if [ -f profile.out ]; then
|
||||
cat profile.out >> coverage.txt
|
||||
rm profile.out
|
||||
fi
|
||||
done
|
||||
|
||||
echo "Publishing go code coverage"
|
||||
bash <(curl -s https://codecov.io/bash) -cF go
|
||||
go test ./pkg/...
|
||||
|
@ -1162,6 +1162,10 @@ balanced-match@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767"
|
||||
|
||||
baron@^3.0.3:
|
||||
version "3.0.3"
|
||||
resolved "https://registry.yarnpkg.com/baron/-/baron-3.0.3.tgz#0f0a08a567062882e130a0ecfd41a46d52103f4a"
|
||||
|
||||
base64-arraybuffer@0.1.5:
|
||||
version "0.1.5"
|
||||
resolved "https://registry.yarnpkg.com/base64-arraybuffer/-/base64-arraybuffer-0.1.5.tgz#73926771923b5a19747ad666aa5cd4bf9c6e9ce8"
|
||||
@ -7503,10 +7507,6 @@ pend@~1.2.0:
|
||||
version "1.2.0"
|
||||
resolved "https://registry.yarnpkg.com/pend/-/pend-1.2.0.tgz#7a57eb550a6783f9115331fcf4663d5c8e007a50"
|
||||
|
||||
perfect-scrollbar@^1.2.0:
|
||||
version "1.2.0"
|
||||
resolved "https://registry.yarnpkg.com/perfect-scrollbar/-/perfect-scrollbar-1.2.0.tgz#ad23a2529c17f4535f21d1486f8bc3046e31a9d2"
|
||||
|
||||
performance-now@^0.2.0:
|
||||
version "0.2.0"
|
||||
resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-0.2.0.tgz#33ef30c5c77d4ea21c5a53869d91b56d8f2555e5"
|
||||
|
Loading…
Reference in New Issue
Block a user