mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Merge branch 'master' into 10630_folder_api
This commit is contained in:
commit
3c6bc263dc
@ -1,3 +1,11 @@
|
||||
# 5.0.0-beta4 (2018-02-19)
|
||||
|
||||
### Fixes
|
||||
|
||||
- **Dashboard** Fixed dashboard overwrite permission issue [#10814](https://github.com/grafana/grafana/issues/10814)
|
||||
- **Keyboard shortcuts** Fixed Esc key when in panel edit/view mode [#10945](https://github.com/grafana/grafana/issues/10945)
|
||||
- **Save dashboard** Fixed issue with time range & variable reset after saving [#10946](https://github.com/grafana/grafana/issues/10946)
|
||||
|
||||
# 5.0.0-beta3 (2018-02-16)
|
||||
|
||||
### Fixes
|
||||
|
@ -1,8 +1,5 @@
|
||||
# This file is only an example.
|
||||
# Grafana will never read sample.yaml files
|
||||
|
||||
# # config file version
|
||||
# apiVersion: 1
|
||||
apiVersion: 1
|
||||
|
||||
#providers:
|
||||
# - name: 'default'
|
||||
|
@ -1,8 +1,5 @@
|
||||
# This file is only an example.
|
||||
# Grafana will never read sample.yaml files
|
||||
|
||||
# # config file version
|
||||
# apiVersion: 1
|
||||
apiVersion: 1
|
||||
|
||||
# # list of datasources that should be deleted from the database
|
||||
#deleteDatasources:
|
||||
|
25
docker/blocks/prometheus2/docker-compose.yaml
Normal file
25
docker/blocks/prometheus2/docker-compose.yaml
Normal file
@ -0,0 +1,25 @@
|
||||
prometheus:
|
||||
build: blocks/prometheus2
|
||||
network_mode: host
|
||||
ports:
|
||||
- "9090:9090"
|
||||
|
||||
node_exporter:
|
||||
image: prom/node-exporter
|
||||
network_mode: host
|
||||
ports:
|
||||
- "9100:9100"
|
||||
|
||||
fake-prometheus-data:
|
||||
image: grafana/fake-data-gen
|
||||
network_mode: host
|
||||
ports:
|
||||
- "9091:9091"
|
||||
environment:
|
||||
FD_DATASOURCE: prom
|
||||
|
||||
alertmanager:
|
||||
image: quay.io/prometheus/alertmanager
|
||||
network_mode: host
|
||||
ports:
|
||||
- "9093:9093"
|
@ -16,7 +16,7 @@ weight = 1
|
||||
Description | Download
|
||||
------------ | -------------
|
||||
Stable for Debian-based Linux | [grafana_4.6.3_amd64.deb](https://s3-us-west-2.amazonaws.com/grafana-releases/release/grafana_4.6.3_amd64.deb)
|
||||
Beta for Debian-based Linux | [grafana_5.0.0-beta3_amd64.deb](https://s3-us-west-2.amazonaws.com/grafana-releases/release/grafana_5.0.0-beta3_amd64.deb)
|
||||
Beta for Debian-based Linux | [grafana_5.0.0-beta4_amd64.deb](https://s3-us-west-2.amazonaws.com/grafana-releases/release/grafana_5.0.0-beta4_amd64.deb)
|
||||
|
||||
Read [Upgrading Grafana]({{< relref "installation/upgrading.md" >}}) for tips and guidance on updating an existing
|
||||
installation.
|
||||
@ -33,9 +33,9 @@ sudo dpkg -i grafana_4.6.3_amd64.deb
|
||||
## Install Latest Beta
|
||||
|
||||
```bash
|
||||
wget https://s3-us-west-2.amazonaws.com/grafana-releases/release/grafana_5.0.0-beta3_amd64.deb
|
||||
wget https://s3-us-west-2.amazonaws.com/grafana-releases/release/grafana_5.0.0-beta4_amd64.deb
|
||||
sudo apt-get install -y adduser libfontconfig
|
||||
sudo dpkg -i grafana_5.0.0-beta3_amd64.deb
|
||||
sudo dpkg -i grafana_5.0.0-beta4_amd64.deb
|
||||
|
||||
```
|
||||
## APT Repository
|
||||
|
@ -16,7 +16,7 @@ weight = 2
|
||||
Description | Download
|
||||
------------ | -------------
|
||||
Stable for CentOS / Fedora / OpenSuse / Redhat Linux | [4.6.3 (x86-64 rpm)](https://s3-us-west-2.amazonaws.com/grafana-releases/release/grafana-4.6.3-1.x86_64.rpm)
|
||||
Latest Beta for CentOS / Fedora / OpenSuse / Redhat Linux | [5.0.0-beta3 (x86-64 rpm)](https://s3-us-west-2.amazonaws.com/grafana-releases/release/grafana-5.0.0-beta3.x86_64.rpm)
|
||||
Latest Beta for CentOS / Fedora / OpenSuse / Redhat Linux | [5.0.0-beta4 (x86-64 rpm)](https://s3-us-west-2.amazonaws.com/grafana-releases/release/grafana-5.0.0-beta4.x86_64.rpm)
|
||||
|
||||
Read [Upgrading Grafana]({{< relref "installation/upgrading.md" >}}) for tips and guidance on updating an existing
|
||||
installation.
|
||||
@ -32,7 +32,7 @@ $ sudo yum install https://s3-us-west-2.amazonaws.com/grafana-releases/release/g
|
||||
## Install Beta
|
||||
|
||||
```bash
|
||||
$ sudo yum install https://s3-us-west-2.amazonaws.com/grafana-releases/release/grafana-5.0.0-beta3.x86_64.rpm
|
||||
$ sudo yum install https://s3-us-west-2.amazonaws.com/grafana-releases/release/grafana-5.0.0-beta4.x86_64.rpm
|
||||
```
|
||||
|
||||
Or install manually using `rpm`.
|
||||
|
@ -14,7 +14,7 @@ weight = 3
|
||||
Description | Download
|
||||
------------ | -------------
|
||||
Latest stable package for Windows | [grafana.4.6.3.windows-x64.zip](https://s3-us-west-2.amazonaws.com/grafana-releases/release/grafana-4.6.3.windows-x64.zip)
|
||||
Latest beta package for Windows | [grafana.5.0.0-beta3.windows-x64.zip](https://s3-us-west-2.amazonaws.com/grafana-releases/release/grafana-5.0.0-beta3.windows-x64.zip)
|
||||
Latest beta package for Windows | [grafana.5.0.0-beta4.windows-x64.zip](https://s3-us-west-2.amazonaws.com/grafana-releases/release/grafana-5.0.0-beta4.windows-x64.zip)
|
||||
|
||||
Read [Upgrading Grafana]({{< relref "installation/upgrading.md" >}}) for tips and guidance on updating an existing
|
||||
installation.
|
||||
|
@ -4,7 +4,7 @@
|
||||
"company": "Grafana Labs"
|
||||
},
|
||||
"name": "grafana",
|
||||
"version": "5.0.0-beta3",
|
||||
"version": "5.0.0-beta4",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "http://github.com/grafana/grafana.git"
|
||||
|
@ -1,6 +1,6 @@
|
||||
#! /usr/bin/env bash
|
||||
deb_ver=5.0.0-beta3
|
||||
rpm_ver=5.0.0-beta3
|
||||
deb_ver=5.0.0-beta4
|
||||
rpm_ver=5.0.0-beta4
|
||||
|
||||
wget https://s3-us-west-2.amazonaws.com/grafana-releases/release/grafana_${deb_ver}_amd64.deb
|
||||
|
||||
|
@ -58,21 +58,22 @@ type DataSource struct {
|
||||
}
|
||||
|
||||
var knownDatasourcePlugins map[string]bool = map[string]bool{
|
||||
DS_ES: true,
|
||||
DS_GRAPHITE: true,
|
||||
DS_INFLUXDB: true,
|
||||
DS_INFLUXDB_08: true,
|
||||
DS_KAIROSDB: true,
|
||||
DS_CLOUDWATCH: true,
|
||||
DS_PROMETHEUS: true,
|
||||
DS_OPENTSDB: true,
|
||||
DS_POSTGRES: true,
|
||||
DS_MYSQL: true,
|
||||
"opennms": true,
|
||||
"druid": true,
|
||||
"dalmatinerdb": true,
|
||||
"gnocci": true,
|
||||
"zabbix": true,
|
||||
DS_ES: true,
|
||||
DS_GRAPHITE: true,
|
||||
DS_INFLUXDB: true,
|
||||
DS_INFLUXDB_08: true,
|
||||
DS_KAIROSDB: true,
|
||||
DS_CLOUDWATCH: true,
|
||||
DS_PROMETHEUS: true,
|
||||
DS_OPENTSDB: true,
|
||||
DS_POSTGRES: true,
|
||||
DS_MYSQL: true,
|
||||
"opennms": true,
|
||||
"abhisant-druid-datasource": true,
|
||||
"dalmatinerdb-datasource": true,
|
||||
"gnocci": true,
|
||||
"zabbix": true,
|
||||
"alexanderzobnin-zabbix-datasource": true,
|
||||
"newrelic-app": true,
|
||||
"grafana-datadog-datasource": true,
|
||||
"grafana-simple-json": true,
|
||||
|
@ -35,7 +35,7 @@ type DashboardInputMissingError struct {
|
||||
}
|
||||
|
||||
func (e DashboardInputMissingError) Error() string {
|
||||
return fmt.Sprintf("Dashbord input variable: %v missing from import command", e.VariableName)
|
||||
return fmt.Sprintf("Dashboard input variable: %v missing from import command", e.VariableName)
|
||||
}
|
||||
|
||||
func init() {
|
||||
|
@ -118,6 +118,7 @@ func (dr *dashboardServiceImpl) buildSaveDashboardCommand(dto *SaveDashboardDTO,
|
||||
UserId: dto.User.UserId,
|
||||
FolderId: dash.FolderId,
|
||||
IsFolder: dash.IsFolder,
|
||||
PluginId: dash.PluginId,
|
||||
}
|
||||
|
||||
if !dto.UpdatedAt.IsZero() {
|
||||
|
@ -63,7 +63,7 @@ func (cr *configReader) readConfig() ([]*DashboardsAsConfig, error) {
|
||||
}
|
||||
|
||||
for _, file := range files {
|
||||
if (!strings.HasSuffix(file.Name(), ".yaml") && !strings.HasSuffix(file.Name(), ".yml")) || file.Name() == "sample.yaml" {
|
||||
if !strings.HasSuffix(file.Name(), ".yaml") && !strings.HasSuffix(file.Name(), ".yml") {
|
||||
continue
|
||||
}
|
||||
|
||||
|
@ -1,10 +1,10 @@
|
||||
apiVersion: 1
|
||||
|
||||
providers:
|
||||
- name: 'gasdf'
|
||||
orgId: 2
|
||||
folder: 'developers'
|
||||
editable: true
|
||||
type: file
|
||||
options:
|
||||
path: /var/lib/grafana/dashboards
|
||||
#providers:
|
||||
#- name: 'gasdf'
|
||||
# orgId: 2
|
||||
# folder: 'developers'
|
||||
# editable: true
|
||||
# type: file
|
||||
# options:
|
||||
# path: /var/lib/grafana/dashboards
|
||||
|
@ -24,7 +24,7 @@ func (cr *configReader) readConfig(path string) ([]*DatasourcesAsConfig, error)
|
||||
}
|
||||
|
||||
for _, file := range files {
|
||||
if (strings.HasSuffix(file.Name(), ".yaml") || strings.HasSuffix(file.Name(), ".yml")) && file.Name() != "sample.yaml" {
|
||||
if strings.HasSuffix(file.Name(), ".yaml") || strings.HasSuffix(file.Name(), ".yml") {
|
||||
datasource, err := cr.parseDatasourceConfig(path, file)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -138,7 +138,7 @@ func TestDatasourceAsConfig(t *testing.T) {
|
||||
t.Fatalf("readConfig return an error %v", err)
|
||||
}
|
||||
|
||||
So(len(cfg), ShouldEqual, 2)
|
||||
So(len(cfg), ShouldEqual, 3)
|
||||
|
||||
dsCfg := cfg[0]
|
||||
|
||||
@ -146,6 +146,17 @@ func TestDatasourceAsConfig(t *testing.T) {
|
||||
|
||||
validateDatasource(dsCfg)
|
||||
validateDeleteDatasources(dsCfg)
|
||||
|
||||
dsCount := 0
|
||||
delDsCount := 0
|
||||
|
||||
for _, c := range cfg {
|
||||
dsCount += len(c.Datasources)
|
||||
delDsCount += len(c.DeleteDatasources)
|
||||
}
|
||||
|
||||
So(dsCount, ShouldEqual, 2)
|
||||
So(delDsCount, ShouldEqual, 1)
|
||||
})
|
||||
|
||||
Convey("can read all properties from version 0", func() {
|
||||
|
@ -3,30 +3,30 @@
|
||||
|
||||
apiVersion: 1
|
||||
|
||||
datasources:
|
||||
- name: name
|
||||
type: type
|
||||
access: proxy
|
||||
orgId: 2
|
||||
url: url
|
||||
password: password
|
||||
user: user
|
||||
database: database
|
||||
basicAuth: true
|
||||
basicAuthUser: basic_auth_user
|
||||
basicAuthPassword: basic_auth_password
|
||||
withCredentials: true
|
||||
jsonData:
|
||||
graphiteVersion: "1.1"
|
||||
tlsAuth: true
|
||||
tlsAuthWithCACert: true
|
||||
secureJsonData:
|
||||
tlsCACert: "MjNOcW9RdkbUDHZmpco2HCYzVq9dE+i6Yi+gmUJotq5CDA=="
|
||||
tlsClientCert: "ckN0dGlyMXN503YNfjTcf9CV+GGQneN+xmAclQ=="
|
||||
tlsClientKey: "ZkN4aG1aNkja/gKAB1wlnKFIsy2SRDq4slrM0A=="
|
||||
editable: true
|
||||
version: 10
|
||||
|
||||
deleteDatasources:
|
||||
- name: old-graphite3
|
||||
orgId: 2
|
||||
#datasources:
|
||||
# - name: name
|
||||
# type: type
|
||||
# access: proxy
|
||||
# orgId: 2
|
||||
# url: url
|
||||
# password: password
|
||||
# user: user
|
||||
# database: database
|
||||
# basicAuth: true
|
||||
# basicAuthUser: basic_auth_user
|
||||
# basicAuthPassword: basic_auth_password
|
||||
# withCredentials: true
|
||||
# jsonData:
|
||||
# graphiteVersion: "1.1"
|
||||
# tlsAuth: true
|
||||
# tlsAuthWithCACert: true
|
||||
# secureJsonData:
|
||||
# tlsCACert: "MjNOcW9RdkbUDHZmpco2HCYzVq9dE+i6Yi+gmUJotq5CDA=="
|
||||
# tlsClientCert: "ckN0dGlyMXN503YNfjTcf9CV+GGQneN+xmAclQ=="
|
||||
# tlsClientKey: "ZkN4aG1aNkja/gKAB1wlnKFIsy2SRDq4slrM0A=="
|
||||
# editable: true
|
||||
# version: 10
|
||||
#
|
||||
#deleteDatasources:
|
||||
# - name: old-graphite3
|
||||
# orgId: 2
|
||||
|
@ -210,7 +210,7 @@ func (s *SocialGithub) UserInfo(client *http.Client, token *oauth2.Token) (*Basi
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Error getting user info: %s", err)
|
||||
}
|
||||
data.OrganizationsUrl = s.apiUrl + "/user/orgs"
|
||||
|
||||
userInfo := &BasicUserInfo{
|
||||
Name: data.Login,
|
||||
Login: data.Login,
|
||||
|
@ -70,7 +70,7 @@ func (query *Query) renderTags() []string {
|
||||
} else if tag.Operator == "<" || tag.Operator == ">" {
|
||||
textValue = tag.Value
|
||||
} else {
|
||||
textValue = fmt.Sprintf("'%s'", tag.Value)
|
||||
textValue = fmt.Sprintf("'%s'", strings.Replace(tag.Value, `\`, `\\`, -1))
|
||||
}
|
||||
|
||||
res = append(res, fmt.Sprintf(`%s"%s" %s %s`, str, tag.Key, tag.Operator, textValue))
|
||||
|
@ -170,6 +170,12 @@ func TestInfluxdbQueryBuilder(t *testing.T) {
|
||||
So(strings.Join(query.renderTags(), ""), ShouldEqual, `"key" = 'value'`)
|
||||
})
|
||||
|
||||
Convey("can escape backslashes when rendering string tags", func() {
|
||||
query := &Query{Tags: []*Tag{{Operator: "=", Value: `C:\test\`, Key: "key"}}}
|
||||
|
||||
So(strings.Join(query.renderTags(), ""), ShouldEqual, `"key" = 'C:\\test\\'`)
|
||||
})
|
||||
|
||||
Convey("can render regular measurement", func() {
|
||||
query := &Query{Measurement: `apa`, Policy: "policy"}
|
||||
|
||||
|
@ -15,8 +15,7 @@ const template = `
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="modal-content">
|
||||
<div class="gf-form-group">
|
||||
<div class="modal-content modal-content--has-scroll" grafana-scrollbar>
|
||||
<table class="filter-table form-inline">
|
||||
<thead>
|
||||
<tr>
|
||||
|
@ -20,7 +20,7 @@
|
||||
<div class="search-section__header" ng-show="section.hideHeader"></div>
|
||||
|
||||
<div ng-if="section.expanded">
|
||||
<a ng-repeat="item in section.items" class="search-item" ng-class="{'selected': item.selected}" ng-href="{{::item.url}}">
|
||||
<a ng-repeat="item in section.items" class="search-item" ng-class="{'selected': item.selected}" ng-href="{{::item.url}}" >
|
||||
<div ng-click="ctrl.toggleSelection(item, $event)">
|
||||
<gf-form-switch
|
||||
ng-show="ctrl.editable"
|
||||
|
@ -12,7 +12,7 @@ export class InvitedCtrl {
|
||||
icon: 'gicon gicon-branding',
|
||||
text: 'Invite',
|
||||
subTitle: 'Register your Grafana account',
|
||||
breadcrumbs: [{ title: 'Login', url: '/login' }],
|
||||
breadcrumbs: [{ title: 'Login', url: 'login' }],
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -10,6 +10,13 @@ export class SignUpCtrl {
|
||||
$scope.formModel = {};
|
||||
|
||||
var params = $location.search();
|
||||
|
||||
// validate email is semi ok
|
||||
if (params.email && !params.email.match(/^\S+@\S+$/)) {
|
||||
console.log('invalid email');
|
||||
return;
|
||||
}
|
||||
|
||||
$scope.formModel.orgName = params.email;
|
||||
$scope.formModel.email = params.email;
|
||||
$scope.formModel.username = params.email;
|
||||
|
@ -75,7 +75,7 @@ export class AlertTabCtrl {
|
||||
|
||||
getAlertHistory() {
|
||||
this.backendSrv
|
||||
.get(`/api/annotations?dashboardId=${this.panelCtrl.dashboard.id}&panelId=${this.panel.id}&limit=50`)
|
||||
.get(`/api/annotations?dashboardId=${this.panelCtrl.dashboard.id}&panelId=${this.panel.id}&limit=50&type=alert`)
|
||||
.then(res => {
|
||||
this.alertHistory = _.map(res, ah => {
|
||||
ah.time = this.dashboardSrv.getCurrent().formatDate(ah.time, 'MMM D, YYYY HH:mm:ss');
|
||||
|
@ -63,11 +63,17 @@ export class DashboardMigrator {
|
||||
}
|
||||
|
||||
if (panel.y_format) {
|
||||
if (!panel.y_formats) {
|
||||
panel.y_formats = [];
|
||||
}
|
||||
panel.y_formats[0] = panel.y_format;
|
||||
delete panel.y_format;
|
||||
}
|
||||
|
||||
if (panel.y2_format) {
|
||||
if (!panel.y_formats) {
|
||||
panel.y_formats = [];
|
||||
}
|
||||
panel.y_formats[1] = panel.y2_format;
|
||||
delete panel.y2_format;
|
||||
}
|
||||
|
@ -1,44 +1,26 @@
|
||||
<div class="container">
|
||||
<page-header model="navModel"></page-header>
|
||||
|
||||
<div class="signup-page-background">
|
||||
</div>
|
||||
<div class="page-container page-body">
|
||||
|
||||
<div class="login-content">
|
||||
|
||||
<div class="login-branding">
|
||||
<img src="img/logo_transparent_200x75.png">
|
||||
</div>
|
||||
|
||||
<div class="invite-box">
|
||||
<h3>
|
||||
<i class="fa fa-users"></i>
|
||||
Change active organization
|
||||
</h3>
|
||||
<div class="signup">
|
||||
<div class="login-form">
|
||||
|
||||
<div class="modal-tagline">
|
||||
You have been added to another Organization <br>
|
||||
due to an open invitation!
|
||||
<br><br>
|
||||
You have been added to another Organization due to an open invitation!
|
||||
|
||||
Please select which organization you want to <br>
|
||||
use right now (you can change this later at any time).
|
||||
</div>
|
||||
|
||||
<div style="display: inline-block; width: 400px; margin: 30px 0">
|
||||
<table class="filter-table">
|
||||
<tr ng-repeat="org in orgs">
|
||||
<td class="nobg max-width-btns">
|
||||
<a ng-click="setUsingOrg(org)" class="btn btn-inverse">
|
||||
{{org.name}} ({{org.role}})
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<div ng-repeat="org in orgs">
|
||||
<a ng-click="setUsingOrg(org)" class="btn btn-success">
|
||||
{{org.name}} ({{org.role}})
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
@ -6,6 +6,14 @@ export class SelectOrgCtrl {
|
||||
constructor($scope, backendSrv, contextSrv) {
|
||||
contextSrv.sidemenu = false;
|
||||
|
||||
$scope.navModel = {
|
||||
main: {
|
||||
icon: 'gicon gicon-branding',
|
||||
subTitle: 'Preferences',
|
||||
text: 'Select active organization',
|
||||
},
|
||||
};
|
||||
|
||||
$scope.init = function() {
|
||||
$scope.getUserOrgs();
|
||||
};
|
||||
|
@ -9,9 +9,7 @@
|
||||
<a href="{{dash.importedUrl}}" ng-show="dash.imported">
|
||||
{{dash.title}}
|
||||
</a>
|
||||
<span ng-show="!dash.imported">
|
||||
{{dash.title}}
|
||||
</span>
|
||||
<span ng-show="!dash.imported">{{dash.title}}</span>
|
||||
</td>
|
||||
<td style="text-align: right">
|
||||
<button class="btn btn-secondary btn-small" ng-click="ctrl.import(dash, false)" ng-show="!dash.imported">
|
||||
|
@ -151,7 +151,11 @@ module.directive('graphLegend', function(popoverSrv, $timeout) {
|
||||
|
||||
if (panel.legend.sort) {
|
||||
seriesList = _.sortBy(seriesList, function(series) {
|
||||
return series.stats[panel.legend.sort];
|
||||
let sort = series.stats[panel.legend.sort];
|
||||
if (sort === null) {
|
||||
sort = -Infinity;
|
||||
}
|
||||
return sort;
|
||||
});
|
||||
if (panel.legend.sortDesc) {
|
||||
seriesList = seriesList.reverse();
|
||||
|
@ -43,7 +43,7 @@
|
||||
<div class="gf-form">
|
||||
<label class="gf-form-label width-8">Point Radius</label>
|
||||
<div class="gf-form-select-wrapper max-width-5">
|
||||
<select class="gf-form-input" ng-model="ctrl.panel.pointradius" ng-options="f for f in [1,2,3,4,5,6,7,8,9,10]" ng-change="ctrl.render()" ng-disabled="!ctrl.panel.points"></select>
|
||||
<select class="gf-form-input" ng-model="ctrl.panel.pointradius" ng-options="f for f in [0.5,1,2,3,4,5,6,7,8,9,10]" ng-change="ctrl.render()" ng-disabled="!ctrl.panel.points"></select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -8,13 +8,18 @@ import { getColorScale, getOpacityScale } from './color_scale';
|
||||
|
||||
let module = angular.module('grafana.directives');
|
||||
|
||||
const LEGEND_HEIGHT_PX = 6;
|
||||
const LEGEND_WIDTH_PX = 100;
|
||||
const LEGEND_TICK_SIZE = 0;
|
||||
const LEGEND_VALUE_MARGIN = 0;
|
||||
|
||||
/**
|
||||
* Color legend for heatmap editor.
|
||||
*/
|
||||
module.directive('colorLegend', function() {
|
||||
return {
|
||||
restrict: 'E',
|
||||
template: '<div class="heatmap-color-legend"><svg width="16.8rem" height="24px"></svg></div>',
|
||||
template: '<div class="heatmap-color-legend"><svg width="16.5rem" height="24px"></svg></div>',
|
||||
link: function(scope, elem, attrs) {
|
||||
let ctrl = scope.ctrl;
|
||||
let panel = scope.ctrl.panel;
|
||||
@ -50,7 +55,7 @@ module.directive('colorLegend', function() {
|
||||
module.directive('heatmapLegend', function() {
|
||||
return {
|
||||
restrict: 'E',
|
||||
template: '<div class="heatmap-color-legend"><svg width="100px" height="14px"></svg></div>',
|
||||
template: `<div class="heatmap-color-legend"><svg width="${LEGEND_WIDTH_PX}px" height="${LEGEND_HEIGHT_PX}px"></svg></div>`,
|
||||
link: function(scope, elem, attrs) {
|
||||
let ctrl = scope.ctrl;
|
||||
let panel = scope.ctrl.panel;
|
||||
@ -163,10 +168,10 @@ function drawLegendValues(elem, colorScale, rangeFrom, rangeTo, maxValue, minVal
|
||||
let xAxis = d3
|
||||
.axisBottom(legendValueScale)
|
||||
.tickValues(ticks)
|
||||
.tickSize(2);
|
||||
.tickSize(LEGEND_TICK_SIZE);
|
||||
|
||||
let colorRect = legendElem.find(':first-child');
|
||||
let posY = getSvgElemHeight(legendElem) + 2;
|
||||
let posY = getSvgElemHeight(legendElem) + LEGEND_VALUE_MARGIN;
|
||||
let posX = getSvgElemX(colorRect);
|
||||
|
||||
d3
|
||||
|
@ -66,8 +66,7 @@ export default function link(scope, elem, attrs, ctrl) {
|
||||
height = parseInt(height.replace('px', ''), 10);
|
||||
}
|
||||
|
||||
height -= 5; // padding
|
||||
height -= panel.title ? 24 : 9; // subtract panel title bar
|
||||
height -= panel.legend.show ? 28 : 11; // bottom padding and space for legend
|
||||
|
||||
$heatmap.css('height', height + 'px');
|
||||
|
||||
|
@ -51,6 +51,9 @@ describe('grafanaHeatmap', function() {
|
||||
colorScheme: 'interpolateOranges',
|
||||
fillBackground: false,
|
||||
},
|
||||
legend: {
|
||||
show: false,
|
||||
},
|
||||
xBucketSize: 1000,
|
||||
xBucketNumber: null,
|
||||
yBucketSize: 1,
|
||||
|
@ -67,6 +67,11 @@
|
||||
|
||||
.modal-content {
|
||||
padding: $spacer*2;
|
||||
|
||||
&--has-scroll {
|
||||
max-height: calc(100vh - 400px);
|
||||
position: relative;
|
||||
}
|
||||
}
|
||||
|
||||
// Remove bottom margin if need be
|
||||
|
@ -1,3 +1,5 @@
|
||||
$font-size-heatmap-tick: 11px;
|
||||
|
||||
.heatmap-canvas-wrapper {
|
||||
// position: relative;
|
||||
cursor: crosshair;
|
||||
@ -10,7 +12,7 @@
|
||||
text {
|
||||
fill: $text-color;
|
||||
color: $text-color;
|
||||
font-size: $font-size-sm;
|
||||
font-size: $font-size-heatmap-tick;
|
||||
}
|
||||
|
||||
line {
|
||||
@ -56,12 +58,12 @@
|
||||
.heatmap-legend-wrapper {
|
||||
@include clearfix();
|
||||
margin: 0 $spacer;
|
||||
padding-top: 10px;
|
||||
padding-top: 4px;
|
||||
|
||||
svg {
|
||||
width: 100%;
|
||||
max-width: 300px;
|
||||
height: 33px;
|
||||
height: 18px;
|
||||
float: left;
|
||||
white-space: nowrap;
|
||||
padding-left: 10px;
|
||||
@ -75,7 +77,7 @@
|
||||
text {
|
||||
fill: $text-color;
|
||||
color: $text-color;
|
||||
font-size: $font-size-sm;
|
||||
font-size: $font-size-heatmap-tick;
|
||||
}
|
||||
|
||||
line {
|
||||
|
@ -75,7 +75,7 @@
|
||||
border-radius: 6px;
|
||||
width: 6px;
|
||||
/* there must be 'right' for ps__thumb-y */
|
||||
right: 2px;
|
||||
right: 0px;
|
||||
/* please don't change 'position' */
|
||||
position: absolute;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user