mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Merge branch 'master' into docs_v5.0
This commit is contained in:
@@ -147,8 +147,7 @@ export class AlertRuleItem extends React.Component<AlertRuleItemProps, any> {
|
||||
<div className="alert-rule-item__body">
|
||||
<div className="alert-rule-item__header">
|
||||
<div className="alert-rule-item__name">
|
||||
{rule.canEdit && <a href={ruleUrl}>{this.renderText(rule.name)}</a>}
|
||||
{!rule.canEdit && <span>{this.renderText(rule.name)}</span>}
|
||||
<a href={ruleUrl}>{this.renderText(rule.name)}</a>
|
||||
</div>
|
||||
<div className="alert-rule-item__text">
|
||||
<span className={`${rule.stateClass}`}>{this.renderText(rule.stateText)}</span>
|
||||
@@ -163,24 +162,12 @@ export class AlertRuleItem extends React.Component<AlertRuleItemProps, any> {
|
||||
className="btn btn-small btn-inverse alert-list__btn width-2"
|
||||
title="Pausing an alert rule prevents it from executing"
|
||||
onClick={this.toggleState}
|
||||
disabled={!rule.canEdit}
|
||||
>
|
||||
<i className={stateClass} />
|
||||
</button>
|
||||
{rule.canEdit && (
|
||||
<a className="btn btn-small btn-inverse alert-list__btn width-2" href={ruleUrl} title="Edit alert rule">
|
||||
<i className="icon-gf icon-gf-settings" />
|
||||
</a>
|
||||
)}
|
||||
{!rule.canEdit && (
|
||||
<button
|
||||
className="btn btn-small btn-inverse alert-list__btn width-2"
|
||||
title="Edit alert rule"
|
||||
disabled={true}
|
||||
>
|
||||
<i className="icon-gf icon-gf-settings" />
|
||||
</button>
|
||||
)}
|
||||
<a className="btn btn-small btn-inverse alert-list__btn width-2" href={ruleUrl} title="Edit alert rule">
|
||||
<i className="icon-gf icon-gf-settings" />
|
||||
</a>
|
||||
</div>
|
||||
</li>
|
||||
);
|
||||
|
||||
@@ -82,7 +82,6 @@ exports[`AlertRuleList should render 1 rule 1`] = `
|
||||
>
|
||||
<button
|
||||
className="btn btn-small btn-inverse alert-list__btn width-2"
|
||||
disabled={false}
|
||||
onClick={[Function]}
|
||||
title="Pausing an alert rule prevents it from executing"
|
||||
>
|
||||
|
||||
@@ -87,6 +87,11 @@ export function grafanaAppDirective(playlistSrv, contextSrv, $timeout, $rootScop
|
||||
elem.toggleClass('playlist-active', newValue === true);
|
||||
});
|
||||
|
||||
// check if we are in server side render
|
||||
if (document.cookie.indexOf('renderKey') !== -1) {
|
||||
body.addClass('body--phantomjs');
|
||||
}
|
||||
|
||||
// tooltip removal fix
|
||||
// manage page classes
|
||||
var pageClass;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<div class="dashboard-list">
|
||||
<div class="page-action-bar page-action-bar--narrow" ng-hide="!ctrl.hasFilters && ctrl.sections.length === 0">
|
||||
<div class="page-action-bar page-action-bar--narrow" ng-hide="ctrl.folderId && !ctrl.hasFilters && ctrl.sections.length === 0">
|
||||
<label class="gf-form gf-form--grow gf-form--has-input-icon">
|
||||
<input type="text" class="gf-form-input max-width-30" placeholder="Find Dashboard by name" tabindex="1" give-focus="true" ng-model="ctrl.query.query" ng-model-options="{ debounce: 500 }" spellcheck='false' ng-change="ctrl.onQueryChange()" />
|
||||
<i class="gf-form-input-icon fa fa-search"></i>
|
||||
@@ -52,6 +52,12 @@
|
||||
</em>
|
||||
</div>
|
||||
|
||||
<div class="search-results" ng-show="!ctrl.folderId && !ctrl.hasFilters && ctrl.sections.length === 0">
|
||||
<em class="muted">
|
||||
No dashboards found.
|
||||
</em>
|
||||
</div>
|
||||
|
||||
<div class="search-results" ng-show="ctrl.sections.length > 0">
|
||||
<div class="search-results-filter-row">
|
||||
<gf-form-switch
|
||||
@@ -103,6 +109,7 @@
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div ng-if="ctrl.canSave && ctrl.folderId && !ctrl.hasFilters && ctrl.sections.length === 0">
|
||||
|
||||
@@ -37,7 +37,7 @@ export class ManageDashboardsCtrl {
|
||||
folderUid?: string;
|
||||
|
||||
// if user can add new folders and/or add new dashboards
|
||||
canSave: boolean;
|
||||
canSave = false;
|
||||
|
||||
// if user has editor role or higher
|
||||
isEditor: boolean;
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -43,7 +43,7 @@
|
||||
</tag-filter>
|
||||
</div>
|
||||
|
||||
<div class="search-filter-box">
|
||||
<div class="search-filter-box" ng-if="ctrl.isEditor">
|
||||
<a href="dashboard/new" class="search-filter-box-link">
|
||||
<i class="gicon gicon-dashboard-new"></i> New dashboard
|
||||
</a>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import _ from 'lodash';
|
||||
import coreModule from '../../core_module';
|
||||
import { SearchSrv } from 'app/core/services/search_srv';
|
||||
import { contextSrv } from 'app/core/services/context_srv';
|
||||
import appEvents from 'app/core/app_events';
|
||||
|
||||
export class SearchCtrl {
|
||||
@@ -15,6 +16,7 @@ export class SearchCtrl {
|
||||
ignoreClose: any;
|
||||
isLoading: boolean;
|
||||
initialFolderFilterTitle: string;
|
||||
isEditor: string;
|
||||
|
||||
/** @ngInject */
|
||||
constructor($scope, private $location, private $timeout, private searchSrv: SearchSrv) {
|
||||
@@ -24,6 +26,7 @@ export class SearchCtrl {
|
||||
this.initialFolderFilterTitle = 'All';
|
||||
this.getTags = this.getTags.bind(this);
|
||||
this.onTagSelect = this.onTagSelect.bind(this);
|
||||
this.isEditor = contextSrv.isEditor;
|
||||
}
|
||||
|
||||
closeSearch() {
|
||||
|
||||
@@ -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;
|
||||
@@ -21,8 +28,9 @@ export class SignUpCtrl {
|
||||
$scope.navModel = {
|
||||
main: {
|
||||
icon: 'gicon gicon-branding',
|
||||
text: 'Sign Up',
|
||||
subTitle: 'Register your Grafana account',
|
||||
breadcrumbs: [{ title: 'Login', url: 'login' }, { title: 'Sign Up' }],
|
||||
breadcrumbs: [{ title: 'Login', url: 'login' }],
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ export class AlertSrv {
|
||||
list: any[];
|
||||
|
||||
/** @ngInject */
|
||||
constructor(private $timeout, private $rootScope, private $modal) {
|
||||
constructor(private $timeout, private $rootScope) {
|
||||
this.list = [];
|
||||
}
|
||||
|
||||
@@ -39,7 +39,6 @@ export class AlertSrv {
|
||||
appEvents.on('alert-warning', options => this.set(options[0], options[1], 'warning', 5000));
|
||||
appEvents.on('alert-success', options => this.set(options[0], options[1], 'success', 3000));
|
||||
appEvents.on('alert-error', options => this.set(options[0], options[1], 'error', 7000));
|
||||
appEvents.on('confirm-modal', this.showConfirmModal.bind(this));
|
||||
}
|
||||
|
||||
getIconForSeverity(severity) {
|
||||
@@ -96,45 +95,6 @@ export class AlertSrv {
|
||||
clearAll() {
|
||||
this.list = [];
|
||||
}
|
||||
|
||||
showConfirmModal(payload) {
|
||||
var scope = this.$rootScope.$new();
|
||||
|
||||
scope.onConfirm = function() {
|
||||
payload.onConfirm();
|
||||
scope.dismiss();
|
||||
};
|
||||
|
||||
scope.updateConfirmText = function(value) {
|
||||
scope.confirmTextValid = payload.confirmText.toLowerCase() === value.toLowerCase();
|
||||
};
|
||||
|
||||
scope.title = payload.title;
|
||||
scope.text = payload.text;
|
||||
scope.text2 = payload.text2;
|
||||
scope.confirmText = payload.confirmText;
|
||||
|
||||
scope.onConfirm = payload.onConfirm;
|
||||
scope.onAltAction = payload.onAltAction;
|
||||
scope.altActionText = payload.altActionText;
|
||||
scope.icon = payload.icon || 'fa-check';
|
||||
scope.yesText = payload.yesText || 'Yes';
|
||||
scope.noText = payload.noText || 'Cancel';
|
||||
scope.confirmTextValid = scope.confirmText ? false : true;
|
||||
|
||||
var confirmModal = this.$modal({
|
||||
template: 'public/app/partials/confirm_modal.html',
|
||||
persist: false,
|
||||
modalClass: 'confirm-modal',
|
||||
show: false,
|
||||
scope: scope,
|
||||
keyboard: false,
|
||||
});
|
||||
|
||||
confirmModal.then(function(modalEl) {
|
||||
modalEl.modal('show');
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
coreModule.service('alertSrv', AlertSrv);
|
||||
|
||||
@@ -5,9 +5,11 @@ import coreModule from 'app/core/core_module';
|
||||
import appEvents from 'app/core/app_events';
|
||||
|
||||
import Mousetrap from 'mousetrap';
|
||||
import 'mousetrap-global-bind';
|
||||
|
||||
export class KeybindingSrv {
|
||||
helpModal: boolean;
|
||||
modalOpen = false;
|
||||
|
||||
/** @ngInject */
|
||||
constructor(private $rootScope, private $location) {
|
||||
@@ -19,6 +21,7 @@ export class KeybindingSrv {
|
||||
});
|
||||
|
||||
this.setupGlobal();
|
||||
appEvents.on('show-modal', () => (this.modalOpen = true));
|
||||
}
|
||||
|
||||
setupGlobal() {
|
||||
@@ -30,6 +33,7 @@ export class KeybindingSrv {
|
||||
this.bind('s o', this.openSearch);
|
||||
this.bind('s t', this.openSearchTags);
|
||||
this.bind('f', this.openSearch);
|
||||
this.bindGlobal('esc', this.exit);
|
||||
}
|
||||
|
||||
openSearchStarred() {
|
||||
@@ -60,6 +64,28 @@ export class KeybindingSrv {
|
||||
appEvents.emit('show-modal', { templateHtml: '<help-modal></help-modal>' });
|
||||
}
|
||||
|
||||
exit() {
|
||||
var popups = $('.popover.in');
|
||||
if (popups.length > 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
appEvents.emit('hide-modal');
|
||||
|
||||
if (!this.modalOpen) {
|
||||
this.$rootScope.appEvent('panel-change-view', { fullscreen: false, edit: false });
|
||||
} else {
|
||||
this.modalOpen = false;
|
||||
}
|
||||
|
||||
// close settings view
|
||||
var search = this.$location.search();
|
||||
if (search.editview) {
|
||||
delete search.editview;
|
||||
this.$location.search(search);
|
||||
}
|
||||
}
|
||||
|
||||
bind(keyArg, fn) {
|
||||
Mousetrap.bind(
|
||||
keyArg,
|
||||
@@ -73,6 +99,19 @@ export class KeybindingSrv {
|
||||
);
|
||||
}
|
||||
|
||||
bindGlobal(keyArg, fn) {
|
||||
Mousetrap.bindGlobal(
|
||||
keyArg,
|
||||
evt => {
|
||||
evt.preventDefault();
|
||||
evt.stopPropagation();
|
||||
evt.returnValue = false;
|
||||
return this.$rootScope.$apply(fn.bind(this));
|
||||
},
|
||||
'keydown'
|
||||
);
|
||||
}
|
||||
|
||||
showDashEditView() {
|
||||
var search = _.extend(this.$location.search(), { editview: 'settings' });
|
||||
this.$location.search(search);
|
||||
@@ -204,23 +243,6 @@ export class KeybindingSrv {
|
||||
this.bind('d v', () => {
|
||||
appEvents.emit('toggle-view-mode');
|
||||
});
|
||||
|
||||
this.bind('esc', () => {
|
||||
var popups = $('.popover.in');
|
||||
if (popups.length > 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
scope.appEvent('hide-modal');
|
||||
scope.appEvent('panel-change-view', { fullscreen: false, edit: false });
|
||||
|
||||
// close settings view
|
||||
var search = this.$location.search();
|
||||
if (search.editview) {
|
||||
delete search.editview;
|
||||
this.$location.search(search);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@ export class UtilSrv {
|
||||
init() {
|
||||
appEvents.on('show-modal', this.showModal.bind(this), this.$rootScope);
|
||||
appEvents.on('hide-modal', this.hideModal.bind(this), this.$rootScope);
|
||||
appEvents.on('confirm-modal', this.showConfirmModal.bind(this), this.$rootScope);
|
||||
}
|
||||
|
||||
hideModal() {
|
||||
@@ -47,6 +48,38 @@ export class UtilSrv {
|
||||
modalEl.modal('show');
|
||||
});
|
||||
}
|
||||
|
||||
showConfirmModal(payload) {
|
||||
var scope = this.$rootScope.$new();
|
||||
|
||||
scope.onConfirm = function() {
|
||||
payload.onConfirm();
|
||||
scope.dismiss();
|
||||
};
|
||||
|
||||
scope.updateConfirmText = function(value) {
|
||||
scope.confirmTextValid = payload.confirmText.toLowerCase() === value.toLowerCase();
|
||||
};
|
||||
|
||||
scope.title = payload.title;
|
||||
scope.text = payload.text;
|
||||
scope.text2 = payload.text2;
|
||||
scope.confirmText = payload.confirmText;
|
||||
|
||||
scope.onConfirm = payload.onConfirm;
|
||||
scope.onAltAction = payload.onAltAction;
|
||||
scope.altActionText = payload.altActionText;
|
||||
scope.icon = payload.icon || 'fa-check';
|
||||
scope.yesText = payload.yesText || 'Yes';
|
||||
scope.noText = payload.noText || 'Cancel';
|
||||
scope.confirmTextValid = scope.confirmText ? false : true;
|
||||
|
||||
appEvents.emit('show-modal', {
|
||||
src: 'public/app/partials/confirm_modal.html',
|
||||
scope: scope,
|
||||
modalClass: 'confirm-modal',
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
coreModule.service('utilSrv', UtilSrv);
|
||||
|
||||
@@ -348,3 +348,10 @@ describe('duration', function() {
|
||||
expect(str).toBe('1 year, 2 months, 0 weeks, 3 days, 4 hours, 5 minutes, 6 seconds, 7 milliseconds');
|
||||
});
|
||||
});
|
||||
|
||||
describe('volume', function() {
|
||||
it('1000m3', function() {
|
||||
var str = kbn.valueFormats['m3'](1000, 1, null);
|
||||
expect(str).toBe('1000.0 m3');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,6 +1,12 @@
|
||||
import { SearchCtrl } from '../components/search/search';
|
||||
import { SearchSrv } from '../services/search_srv';
|
||||
|
||||
jest.mock('app/core/services/context_srv', () => ({
|
||||
contextSrv: {
|
||||
user: { orgId: 1 },
|
||||
},
|
||||
}));
|
||||
|
||||
describe('SearchCtrl', () => {
|
||||
const searchSrvStub = {
|
||||
search: (options: any) => {},
|
||||
|
||||
@@ -547,8 +547,8 @@ kbn.valueFormats.accG = kbn.formatBuilders.fixedUnit('g');
|
||||
// Volume
|
||||
kbn.valueFormats.litre = kbn.formatBuilders.decimalSIPrefix('L');
|
||||
kbn.valueFormats.mlitre = kbn.formatBuilders.decimalSIPrefix('L', -1);
|
||||
kbn.valueFormats.m3 = kbn.formatBuilders.decimalSIPrefix('m3');
|
||||
kbn.valueFormats.dm3 = kbn.formatBuilders.decimalSIPrefix('dm3');
|
||||
kbn.valueFormats.m3 = kbn.formatBuilders.fixedUnit('m3');
|
||||
kbn.valueFormats.dm3 = kbn.formatBuilders.fixedUnit('dm3');
|
||||
kbn.valueFormats.gallons = kbn.formatBuilders.fixedUnit('gal');
|
||||
|
||||
// Flow
|
||||
|
||||
@@ -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');
|
||||
|
||||
@@ -58,15 +58,29 @@ export class AlertNotificationEditCtrl {
|
||||
}
|
||||
|
||||
if (this.model.id) {
|
||||
this.backendSrv.put(`/api/alert-notifications/${this.model.id}`, this.model).then(res => {
|
||||
this.model = res;
|
||||
appEvents.emit('alert-success', ['Notification updated', '']);
|
||||
});
|
||||
this.backendSrv
|
||||
.put(`/api/alert-notifications/${this.model.id}`, this.model)
|
||||
.then(res => {
|
||||
this.model = res;
|
||||
appEvents.emit('alert-success', ['Notification updated', '']);
|
||||
})
|
||||
.catch(err => {
|
||||
if (err.data && err.data.error) {
|
||||
appEvents.emit('alert-error', [err.data.error]);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
this.backendSrv.post(`/api/alert-notifications`, this.model).then(res => {
|
||||
appEvents.emit('alert-success', ['Notification created', '']);
|
||||
this.$location.path('alerting/notifications');
|
||||
});
|
||||
this.backendSrv
|
||||
.post(`/api/alert-notifications`, this.model)
|
||||
.then(res => {
|
||||
appEvents.emit('alert-success', ['Notification created', '']);
|
||||
this.$location.path('alerting/notifications');
|
||||
})
|
||||
.catch(err => {
|
||||
if (err.data && err.data.error) {
|
||||
appEvents.emit('alert-error', [err.data.error]);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ export class DashboardMigrator {
|
||||
}
|
||||
|
||||
updateSchema(old) {
|
||||
var i, j, k;
|
||||
var i, j, k, n;
|
||||
var oldVersion = this.dashboard.schemaVersion;
|
||||
var panelUpgrades = [];
|
||||
this.dashboard.schemaVersion = 16;
|
||||
@@ -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;
|
||||
}
|
||||
@@ -372,6 +378,11 @@ export class DashboardMigrator {
|
||||
for (j = 0; j < this.dashboard.panels.length; j++) {
|
||||
for (k = 0; k < panelUpgrades.length; k++) {
|
||||
panelUpgrades[k].call(this, this.dashboard.panels[j]);
|
||||
if (this.dashboard.panels[j].panels) {
|
||||
for (n = 0; n < this.dashboard.panels[j].panels.length; n++) {
|
||||
panelUpgrades[k].call(this, this.dashboard.panels[j].panels[n]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -429,6 +440,9 @@ export class DashboardMigrator {
|
||||
|
||||
for (let panel of row.panels) {
|
||||
panel.span = panel.span || DEFAULT_PANEL_SPAN;
|
||||
if (panel.minSpan) {
|
||||
panel.minSpan = Math.min(GRID_COLUMN_COUNT, GRID_COLUMN_COUNT / 12 * panel.minSpan);
|
||||
}
|
||||
const panelWidth = Math.floor(panel.span) * widthFactor;
|
||||
const panelHeight = panel.height ? getGridHeight(panel.height) : rowGridHeight;
|
||||
|
||||
|
||||
@@ -500,11 +500,12 @@ export class DashboardModel {
|
||||
if (!rowPanel.panels || rowPanel.panels.length === 0) {
|
||||
return 0;
|
||||
}
|
||||
const rowYPos = rowPanel.gridPos.y;
|
||||
const positions = _.map(rowPanel.panels, 'gridPos');
|
||||
const maxPos = _.maxBy(positions, pos => {
|
||||
return pos.y + pos.h;
|
||||
});
|
||||
return maxPos.h + 1;
|
||||
return maxPos.y + maxPos.h - rowYPos;
|
||||
}
|
||||
|
||||
removePanel(panel: PanelModel) {
|
||||
|
||||
@@ -77,8 +77,11 @@ export class DashboardSrv {
|
||||
postSave(clone, data) {
|
||||
this.dash.version = data.version;
|
||||
|
||||
if (data.url !== this.$location.path()) {
|
||||
this.$location.url(locationUtil.stripBaseFromUrl(data.url)).replace();
|
||||
const newUrl = locationUtil.stripBaseFromUrl(data.url);
|
||||
const currentPath = this.$location.path();
|
||||
|
||||
if (newUrl !== currentPath) {
|
||||
this.$location.url(newUrl).replace();
|
||||
}
|
||||
|
||||
this.$rootScope.appEvent('dashboard-saved', this.dash);
|
||||
|
||||
@@ -30,7 +30,13 @@ export class FolderPickerCtrl {
|
||||
}
|
||||
|
||||
getOptions(query) {
|
||||
return this.backendSrv.get('api/dashboards/folders', { query: query }).then(result => {
|
||||
const params = {
|
||||
query: query,
|
||||
type: 'dash-folder',
|
||||
permission: 'Edit',
|
||||
};
|
||||
|
||||
return this.backendSrv.get('api/search', params).then(result => {
|
||||
if (
|
||||
query === '' ||
|
||||
query.toLowerCase() === 'g' ||
|
||||
|
||||
@@ -363,6 +363,22 @@ describe('DashboardModel', function() {
|
||||
expect(dashboard.panels[0].repeat).toBe('server');
|
||||
expect(dashboard.panels.length).toBe(2);
|
||||
});
|
||||
|
||||
it('minSpan should be twice', function() {
|
||||
model.rows = [createRow({ height: 8 }, [[6]])];
|
||||
model.rows[0].panels[0] = { minSpan: 12 };
|
||||
|
||||
let dashboard = new DashboardModel(model);
|
||||
expect(dashboard.panels[0].minSpan).toBe(24);
|
||||
});
|
||||
|
||||
it('should assign id', function() {
|
||||
model.rows = [createRow({ collapse: true, height: 8 }, [[6], [6]])];
|
||||
model.rows[0].panels[0] = {};
|
||||
|
||||
let dashboard = new DashboardModel(model);
|
||||
expect(dashboard.panels[0].id).toBe(1);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -500,6 +500,22 @@ describe('given dashboard with row repeat', function() {
|
||||
);
|
||||
expect(panel_ids.length).toEqual(_.uniq(panel_ids).length);
|
||||
});
|
||||
|
||||
it('should place new panels in proper order', function() {
|
||||
dashboardJSON.panels = [
|
||||
{ id: 1, type: 'row', gridPos: { x: 0, y: 0, h: 1, w: 24 }, repeat: 'apps' },
|
||||
{ id: 2, type: 'graph', gridPos: { x: 0, y: 1, h: 3, w: 12 } },
|
||||
{ id: 3, type: 'graph', gridPos: { x: 6, y: 1, h: 4, w: 12 } },
|
||||
{ id: 4, type: 'graph', gridPos: { x: 0, y: 5, h: 2, w: 12 } },
|
||||
];
|
||||
dashboard = new DashboardModel(dashboardJSON);
|
||||
dashboard.processRepeats();
|
||||
|
||||
const panel_types = _.map(dashboard.panels, 'type');
|
||||
expect(panel_types).toEqual(['row', 'graph', 'graph', 'graph', 'row', 'graph', 'graph', 'graph']);
|
||||
const panel_y_positions = _.map(dashboard.panels, p => p.gridPos.y);
|
||||
expect(panel_y_positions).toEqual([0, 1, 1, 5, 7, 8, 8, 12]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('given dashboard with row and panel repeat', () => {
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
</div>
|
||||
<div class="gf-form" ng-show="ctrl.panel.repeat">
|
||||
<span class="gf-form-label width-9">Min width</span>
|
||||
<select class="gf-form-input" ng-model="ctrl.panel.minSpan" ng-options="f for f in [1,2,3,4,5,6,7,8,9,10,11,12]">
|
||||
<select class="gf-form-input" ng-model="ctrl.panel.minSpan" ng-options="f for f in [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24]">
|
||||
<option value=""></option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
|
||||
</div>
|
||||
</form>
|
||||
<div ng-if="mode === 'email-sent'">
|
||||
<div ng-show="mode === 'email-sent'">
|
||||
An email with a reset link as been sent to the email address. <br>
|
||||
You should receive it shortly.
|
||||
<div class="p-t-1">
|
||||
@@ -27,5 +27,23 @@
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<form name="resetForm" class="login-form gf-form-group" ng-show="mode === 'reset'">
|
||||
<div class="gf-form">
|
||||
<span class="gf-form-label width-9">New Password</span>
|
||||
<input type="password" name="NewPassword" class="gf-form-input max-width-14" required ng-minlength="4" ng-model='formModel.newPassword' placeholder="password" watch-change="formModel.newPassword = inputValue;">
|
||||
</div>
|
||||
<div class="gf-form">
|
||||
<span class="gf-form-label width-9">Confirm Password</span>
|
||||
<input type="password" name="ConfirmPassword" class="gf-form-input max-width-14" required ng-minlength="4" ng-model='formModel.confirmPassword' placeholder="confirm password">
|
||||
</div>
|
||||
<div class="signup__password-strength">
|
||||
<password-strength password="formModel.newPassword"></password-strength>
|
||||
</div>
|
||||
<div class="gf-form-button-row">
|
||||
<button type="submit" class="btn btn-success" ng-click="submitReset();" ng-disabled="!resetForm.$valid">
|
||||
Reset Password
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# CloudWatch Datasource - Native Plugin
|
||||
# CloudWatch Data Source - Native Plugin
|
||||
|
||||
Grafana ships with **built in** support for CloudWatch. You just have to add it as a data source and you will be ready to build dashboards for you CloudWatch metrics.
|
||||
|
||||
Read more about it here:
|
||||
|
||||
[http://docs.grafana.org/datasources/cloudwatch/](http://docs.grafana.org/datasources/cloudwatch/)
|
||||
[http://docs.grafana.org/datasources/cloudwatch/](http://docs.grafana.org/datasources/cloudwatch/)
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
"annotations": true,
|
||||
|
||||
"info": {
|
||||
"description": "Cloudwatch Data Source for Grafana",
|
||||
"author": {
|
||||
"name": "Grafana Project",
|
||||
"url": "https://grafana.com"
|
||||
@@ -15,6 +16,7 @@
|
||||
"logos": {
|
||||
"small": "img/amazon-web-services.png",
|
||||
"large": "img/amazon-web-services.png"
|
||||
}
|
||||
},
|
||||
"version": "5.0.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Elasticsearch Datasource - Native Plugin
|
||||
# Elasticsearch Data Source - Native Plugin
|
||||
|
||||
Grafana ships with **advanced support** for Elasticsearch. You can do many types of simple or complex elasticsearch queries to visualize logs or metrics stored in Elasticsearch. You can also annotate your graphs with log events stored in Elasticsearch.
|
||||
|
||||
|
||||
@@ -166,7 +166,7 @@ export class ElasticDatasource {
|
||||
|
||||
for (var i = 0; i < hits.length; i++) {
|
||||
var source = hits[i]._source;
|
||||
var time = source[timeField];
|
||||
var time = getFieldFromSource(source, timeField);
|
||||
if (typeof hits[i].fields !== 'undefined') {
|
||||
var fields = hits[i].fields;
|
||||
if (_.isString(fields[timeField]) || _.isNumber(fields[timeField])) {
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
"links": [
|
||||
{"name": "elastic.co", "url": "https://www.elastic.co/products/elasticsearch"}
|
||||
],
|
||||
"version": "3.0.0"
|
||||
"version": "5.0.0"
|
||||
},
|
||||
|
||||
"annotations": true,
|
||||
|
||||
@@ -6,4 +6,8 @@ Grafana has an advanced Graphite query editor that lets you quickly navigate the
|
||||
|
||||
Read more about it here:
|
||||
|
||||
[http://docs.grafana.org/datasources/graphite/](http://docs.grafana.org/datasources/graphite/)
|
||||
[http://docs.grafana.org/datasources/graphite/](http://docs.grafana.org/datasources/graphite/)
|
||||
|
||||
Graphite 1.1 Release:
|
||||
|
||||
[https://grafana.com/blog/2018/01/11/graphite-1.1-teaching-an-old-dog-new-tricks/](https://grafana.com/blog/2018/01/11/graphite-1.1-teaching-an-old-dog-new-tricks/)
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
},
|
||||
|
||||
"info": {
|
||||
"description": "Graphite Data Source for Grafana",
|
||||
"author": {
|
||||
"name": "Grafana Project",
|
||||
"url": "https://grafana.com"
|
||||
@@ -24,6 +25,11 @@
|
||||
"logos": {
|
||||
"small": "img/graphite_logo.png",
|
||||
"large": "img/graphite_logo.png"
|
||||
}
|
||||
},
|
||||
"links": [
|
||||
{"name": "Graphite", "url": "https://graphiteapp.org/"},
|
||||
{"name": "Graphite 1.1 Release", "url": "https://grafana.com/blog/2018/01/11/graphite-1.1-teaching-an-old-dog-new-tricks/"}
|
||||
],
|
||||
"version": "5.0.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
# InfluxDB Datasource - Native Plugin
|
||||
|
||||
Grafana ships with **built in** support for InfluxDB 0.9.
|
||||
Grafana ships with **built in** support for InfluxDB (> 0.9.x).
|
||||
|
||||
There are currently two separate datasources for InfluxDB in Grafana: InfluxDB 0.8.x and InfluxDB 0.9.x. The API and capabilities of InfluxDB 0.9.x are completely different from InfluxDB 0.8.x which is why Grafana handles them as different data sources.
|
||||
|
||||
This is the plugin for InfluxDB 0.9. It is rapidly evolving and we continue to track its API.
|
||||
There are currently two separate datasources for InfluxDB in Grafana: InfluxDB 0.8.x and the latest InfluxDB release. The API and capabilities of latest (> 0.9.x) InfluxDB are completely different from InfluxDB 0.8.x which is why Grafana handles them as different data sources.
|
||||
|
||||
InfluxDB 0.8 is no longer maintained by InfluxDB Inc, but we provide support as a convenience to existing users. You can find it [here](https://grafana.com/plugins/grafana-influxdb-08-datasource).
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
},
|
||||
|
||||
"info": {
|
||||
"description": "InfluxDB Data Source for Grafana",
|
||||
"author": {
|
||||
"name": "Grafana Project",
|
||||
"url": "https://grafana.com"
|
||||
@@ -20,6 +21,7 @@
|
||||
"logos": {
|
||||
"small": "img/influxdb_logo.svg",
|
||||
"large": "img/influxdb_logo.svg"
|
||||
}
|
||||
},
|
||||
"version": "5.0.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,14 @@
|
||||
# Grafana Fake Data Datasource - Native Plugin
|
||||
# MySQL Data Source - Native Plugin
|
||||
|
||||
This is the built in Fake Data Datasource that is used before any datasources are set up in your Grafana installation. It means you can create a graph without any data and still get an idea of what it would look like.
|
||||
Grafana ships with a built-in MySQL data source plugin that allow you to query any visualize data from a MySQL compatible database.
|
||||
|
||||
## Adding the data source
|
||||
|
||||
1. Open the side menu by clicking the Grafana icon in the top header.
|
||||
2. In the side menu under the Dashboards link you should find a link named Data Sources.
|
||||
3. Click the + Add data source button in the top header.
|
||||
4. Select MySQL from the Type dropdown.
|
||||
|
||||
Read more about it here:
|
||||
|
||||
[http://docs.grafana.org/features/datasources/mysql/](http://docs.grafana.org/features/datasources/mysql/)
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
"id": "mysql",
|
||||
|
||||
"info": {
|
||||
"description": "MySQL Data Source for Grafana",
|
||||
"author": {
|
||||
"name": "Grafana Project",
|
||||
"url": "https://grafana.com"
|
||||
@@ -11,7 +12,8 @@
|
||||
"logos": {
|
||||
"small": "img/mysql_logo.svg",
|
||||
"large": "img/mysql_logo.svg"
|
||||
}
|
||||
},
|
||||
"version": "5.0.0"
|
||||
},
|
||||
|
||||
"alerting": true,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# OpenTSDB Datasource - Native Plugin
|
||||
# OpenTSDB Data Source - Native Plugin
|
||||
|
||||
Grafana ships with **built in** support for OpenTSDB, a scalable, distributed time series database.
|
||||
|
||||
Read more about it here:
|
||||
|
||||
[http://docs.grafana.org/datasources/opentsdb/](http://docs.grafana.org/datasources/opentsdb/)
|
||||
[http://docs.grafana.org/datasources/opentsdb/](http://docs.grafana.org/datasources/opentsdb/)
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
"alerting": true,
|
||||
|
||||
"info": {
|
||||
"description": "OpenTSDB Data Source for Grafana",
|
||||
"author": {
|
||||
"name": "Grafana Project",
|
||||
"url": "https://grafana.com"
|
||||
@@ -16,6 +17,7 @@
|
||||
"logos": {
|
||||
"small": "img/opentsdb_logo.png",
|
||||
"large": "img/opentsdb_logo.png"
|
||||
}
|
||||
},
|
||||
"version": "5.0.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,12 @@
|
||||
# Grafana PostgreSQL Datasource - Native Plugin
|
||||
# Grafana PostgreSQL Data Source - Native Plugin
|
||||
|
||||
This is the built in PostgreSQL Datasource that is used to connect to PostgreSQL databases.
|
||||
Grafana ships with a built-in PostgreSQL data source plugin that allows you to query and visualize data from a PostgreSQL compatible database.
|
||||
|
||||
## Adding the data source
|
||||
|
||||
1. Open the side menu by clicking the Grafana icon in the top header.
|
||||
2. In the side menu under the Dashboards link you should find a link named Data Sources.
|
||||
3. Click the + Add data source button in the top header.
|
||||
4. Select PostgreSQL from the Type dropdown.
|
||||
|
||||
[http://docs.grafana.org/features/datasources/postgres/](http://docs.grafana.org/features/datasources/postgres/)
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
"id": "postgres",
|
||||
|
||||
"info": {
|
||||
"description": "PostgreSQL Data Source for Grafana",
|
||||
"author": {
|
||||
"name": "Grafana Project",
|
||||
"url": "https://grafana.com"
|
||||
@@ -11,7 +12,8 @@
|
||||
"logos": {
|
||||
"small": "img/postgresql_logo.svg",
|
||||
"large": "img/postgresql_logo.svg"
|
||||
}
|
||||
},
|
||||
"version": "5.0.0"
|
||||
},
|
||||
|
||||
"alerting": true,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Prometheus Datasource - Native Plugin
|
||||
# Prometheus Data Source - Native Plugin
|
||||
|
||||
Grafana ships with **built in** support for Prometheus, the open-source service monitoring system and time series database.
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
},
|
||||
|
||||
"info": {
|
||||
"description": "Prometheus Data Source for Grafana",
|
||||
"author": {
|
||||
"name": "Grafana Project",
|
||||
"url": "https://grafana.com"
|
||||
@@ -25,6 +26,10 @@
|
||||
"logos": {
|
||||
"small": "img/prometheus_logo.svg",
|
||||
"large": "img/prometheus_logo.svg"
|
||||
}
|
||||
},
|
||||
"links": [
|
||||
{"name": "Prometheus", "url": "https://prometheus.io/"}
|
||||
],
|
||||
"version": "5.0.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1 +1,9 @@
|
||||
# Alert List Panel - Native plugin
|
||||
|
||||
This Alert List panel is **included** with Grafana.
|
||||
|
||||
The Alert List panel allows you to display alerts on a dashboard. The list can be configured to show either the current state of your alerts or recent alert state changes. You can read more about alerts [here](http://docs.grafana.org/alerting/rules).
|
||||
|
||||
Read more about it here:
|
||||
|
||||
[http://docs.grafana.org/features/panels/alertlist/](http://docs.grafana.org/features/panels/alertlist/)
|
||||
|
||||
@@ -4,13 +4,15 @@
|
||||
"id": "alertlist",
|
||||
|
||||
"info": {
|
||||
"description": "Shows list of alerts and their current status",
|
||||
"author": {
|
||||
"name": "Grafana Project",
|
||||
"url": "https://grafana.com"
|
||||
},
|
||||
},
|
||||
"logos": {
|
||||
"small": "img/icn-singlestat-panel.svg",
|
||||
"large": "img/icn-singlestat-panel.svg"
|
||||
}
|
||||
},
|
||||
"version": "5.0.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
"id": "dashlist",
|
||||
|
||||
"info": {
|
||||
"description": "List of dynamic links to other dashboards",
|
||||
"author": {
|
||||
"name": "Grafana Project",
|
||||
"url": "https://grafana.com"
|
||||
@@ -11,6 +12,7 @@
|
||||
"logos": {
|
||||
"small": "img/icn-dashlist-panel.svg",
|
||||
"large": "img/icn-dashlist-panel.svg"
|
||||
}
|
||||
},
|
||||
"version": "5.0.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
"id": "graph",
|
||||
|
||||
"info": {
|
||||
"description": "Graph Panel for Grafana",
|
||||
"author": {
|
||||
"name": "Grafana Project",
|
||||
"url": "https://grafana.com"
|
||||
@@ -11,7 +12,8 @@
|
||||
"logos": {
|
||||
"small": "img/icn-graph-panel.svg",
|
||||
"large": "img/icn-graph-panel.svg"
|
||||
}
|
||||
},
|
||||
"version": "5.0.0"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
# Heatmap Panel - Native Plugin
|
||||
|
||||
The Heatmap panel allows you to view histograms over time and is **included** with Grafana.
|
||||
|
||||
Read more about it here:
|
||||
|
||||
[http://docs.grafana.org/features/panels/heatmap/](http://docs.grafana.org/features/panels/heatmap/)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
"id": "heatmap",
|
||||
|
||||
"info": {
|
||||
"description": "Heatmap Panel for Grafana",
|
||||
"author": {
|
||||
"name": "Grafana Project",
|
||||
"url": "https://grafana.com"
|
||||
@@ -11,6 +12,11 @@
|
||||
"logos": {
|
||||
"small": "img/icn-heatmap-panel.svg",
|
||||
"large": "img/icn-heatmap-panel.svg"
|
||||
}
|
||||
},
|
||||
"links": [
|
||||
{"name": "Brendan Gregg - Heatmaps", "url": "http://www.brendangregg.com/heatmaps.html"},
|
||||
{"name": "Brendan Gregg - Latency Heatmaps", "url": " http://www.brendangregg.com/HeatMaps/latency.html"}
|
||||
],
|
||||
"version": "5.0.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -1,2 +1,3 @@
|
||||
# Plugin List Panel - Native Plugin
|
||||
|
||||
The Plugin List plans shows the installed plugins for your Grafana instance and is **included** with Grafana. It is used on the default Home dashboard.
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
"id": "pluginlist",
|
||||
|
||||
"info": {
|
||||
"description": "Plugin List for Grafana",
|
||||
"author": {
|
||||
"name": "Grafana Project",
|
||||
"url": "https://grafana.com"
|
||||
@@ -11,6 +12,7 @@
|
||||
"logos": {
|
||||
"small": "img/icn-dashlist-panel.svg",
|
||||
"large": "img/icn-dashlist-panel.svg"
|
||||
}
|
||||
},
|
||||
"version": "5.0.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
"id": "singlestat",
|
||||
|
||||
"info": {
|
||||
"description": "Singlestat Panel for Grafana",
|
||||
"author": {
|
||||
"name": "Grafana Project",
|
||||
"url": "https://grafana.com"
|
||||
@@ -11,7 +12,8 @@
|
||||
"logos": {
|
||||
"small": "img/icn-singlestat-panel.svg",
|
||||
"large": "img/icn-singlestat-panel.svg"
|
||||
}
|
||||
},
|
||||
"version": "5.0.0"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -6,4 +6,4 @@ The table panel is very flexible, supporting both multiple modes for time series
|
||||
|
||||
Check out the [Table Panel Showcase in the Grafana Playground](http://play.grafana.org/dashboard/db/table-panel-showcase) or read more about it here:
|
||||
|
||||
[http://docs.grafana.org/reference/table_panel/](http://docs.grafana.org/reference/table_panel/)
|
||||
[http://docs.grafana.org/reference/table_panel/](http://docs.grafana.org/reference/table_panel/)
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
"id": "table",
|
||||
|
||||
"info": {
|
||||
"description": "Table Panel for Grafana",
|
||||
"author": {
|
||||
"name": "Grafana Project",
|
||||
"url": "https://grafana.com"
|
||||
@@ -11,7 +12,8 @@
|
||||
"logos": {
|
||||
"small": "img/icn-table-panel.svg",
|
||||
"large": "img/icn-table-panel.svg"
|
||||
}
|
||||
},
|
||||
"version": "5.0.0"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -11,7 +11,8 @@
|
||||
"logos": {
|
||||
"small": "img/icn-text-panel.svg",
|
||||
"large": "img/icn-text-panel.svg"
|
||||
}
|
||||
},
|
||||
"version": "5.0.0"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -14,7 +14,6 @@ export const AlertRule = types
|
||||
stateAge: types.string,
|
||||
info: types.optional(types.string, ''),
|
||||
url: types.string,
|
||||
canEdit: types.boolean,
|
||||
})
|
||||
.views(self => ({
|
||||
get isPaused() {
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
import { types } from 'mobx-state-tree';
|
||||
import { types } from 'mobx-state-tree';
|
||||
|
||||
export const PermissionsStoreItem = types
|
||||
.model('PermissionsStoreItem', {
|
||||
dashboardId: types.optional(types.number, -1),
|
||||
id: types.maybe(types.number),
|
||||
permission: types.number,
|
||||
permissionName: types.maybe(types.string),
|
||||
role: types.maybe(types.string),
|
||||
|
||||
@@ -72,7 +72,7 @@ $textShadow: none;
|
||||
|
||||
// gradients
|
||||
$brand-gradient: linear-gradient(to right, rgba(255, 213, 0, 1) 0%, rgba(255, 68, 0, 1) 99%, rgba(255, 68, 0, 1) 100%);
|
||||
$page-gradient: linear-gradient(-60deg, transparent 70%, $gray-7 98%);
|
||||
$page-gradient: linear-gradient(-60deg, $gray-7, #f5f6f9 70%, $gray-7 98%);
|
||||
|
||||
// Links
|
||||
// -------------------------
|
||||
|
||||
@@ -1,290 +1,254 @@
|
||||
@import "base/font_awesome";
|
||||
@import "base/grafana_icons";
|
||||
@import 'font_awesome';
|
||||
@import 'grafana_icons';
|
||||
|
||||
/* cyrillic-ext */
|
||||
@font-face {
|
||||
font-family: "Roboto";
|
||||
font-family: 'Roboto';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: local("Roboto"), local("Roboto-Regular"),
|
||||
url(../fonts/roboto/ek4gzZ-GeXAPcSbHtCeQI_esZW2xOQ-xsNqO47m55DA.woff2)
|
||||
format("woff2");
|
||||
src: local('Roboto'), local('Roboto-Regular'),
|
||||
url(../fonts/roboto/ek4gzZ-GeXAPcSbHtCeQI_esZW2xOQ-xsNqO47m55DA.woff2) format('woff2');
|
||||
unicode-range: U+0460-052f, U+20b4, U+2de0-2dff, U+A640-A69F;
|
||||
}
|
||||
/* cyrillic */
|
||||
@font-face {
|
||||
font-family: "Roboto";
|
||||
font-family: 'Roboto';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: local("Roboto"), local("Roboto-Regular"),
|
||||
url(../fonts/roboto/mErvLBYg_cXG3rLvUsKT_fesZW2xOQ-xsNqO47m55DA.woff2)
|
||||
format("woff2");
|
||||
src: local('Roboto'), local('Roboto-Regular'),
|
||||
url(../fonts/roboto/mErvLBYg_cXG3rLvUsKT_fesZW2xOQ-xsNqO47m55DA.woff2) format('woff2');
|
||||
unicode-range: U+0400-045f, U+0490-0491, U+04b0-04b1, U+2116;
|
||||
}
|
||||
/* greek-ext */
|
||||
@font-face {
|
||||
font-family: "Roboto";
|
||||
font-family: 'Roboto';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: local("Roboto"), local("Roboto-Regular"),
|
||||
url(../fonts/roboto/-2n2p-_Y08sg57CNWQfKNvesZW2xOQ-xsNqO47m55DA.woff2)
|
||||
format("woff2");
|
||||
src: local('Roboto'), local('Roboto-Regular'),
|
||||
url(../fonts/roboto/-2n2p-_Y08sg57CNWQfKNvesZW2xOQ-xsNqO47m55DA.woff2) format('woff2');
|
||||
unicode-range: U+1f00-1fff;
|
||||
}
|
||||
/* greek */
|
||||
@font-face {
|
||||
font-family: "Roboto";
|
||||
font-family: 'Roboto';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: local("Roboto"), local("Roboto-Regular"),
|
||||
url(../fonts/roboto/u0TOpm082MNkS5K0Q4rhqvesZW2xOQ-xsNqO47m55DA.woff2)
|
||||
format("woff2");
|
||||
src: local('Roboto'), local('Roboto-Regular'),
|
||||
url(../fonts/roboto/u0TOpm082MNkS5K0Q4rhqvesZW2xOQ-xsNqO47m55DA.woff2) format('woff2');
|
||||
unicode-range: U+0370-03ff;
|
||||
}
|
||||
/* vietnamese */
|
||||
@font-face {
|
||||
font-family: "Roboto";
|
||||
font-family: 'Roboto';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: local("Roboto"), local("Roboto-Regular"),
|
||||
url(../fonts/roboto/NdF9MtnOpLzo-noMoG0miPesZW2xOQ-xsNqO47m55DA.woff2)
|
||||
format("woff2");
|
||||
src: local('Roboto'), local('Roboto-Regular'),
|
||||
url(../fonts/roboto/NdF9MtnOpLzo-noMoG0miPesZW2xOQ-xsNqO47m55DA.woff2) format('woff2');
|
||||
unicode-range: U+0102-0103, U+1ea0-1ef9, U+20ab;
|
||||
}
|
||||
/* latin-ext */
|
||||
@font-face {
|
||||
font-family: "Roboto";
|
||||
font-family: 'Roboto';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: local("Roboto"), local("Roboto-Regular"),
|
||||
url(../fonts/roboto/Fcx7Wwv8OzT71A3E1XOAjvesZW2xOQ-xsNqO47m55DA.woff2)
|
||||
format("woff2");
|
||||
unicode-range: U+0100-024f, U+1-1eff, U+20a0-20ab, U+20ad-20cf, U+2c60-2c7f,
|
||||
U+A720-A7FF;
|
||||
src: local('Roboto'), local('Roboto-Regular'),
|
||||
url(../fonts/roboto/Fcx7Wwv8OzT71A3E1XOAjvesZW2xOQ-xsNqO47m55DA.woff2) format('woff2');
|
||||
unicode-range: U+0100-024f, U+1-1eff, U+20a0-20ab, U+20ad-20cf, U+2c60-2c7f, U+A720-A7FF;
|
||||
}
|
||||
/* latin */
|
||||
@font-face {
|
||||
font-family: "Roboto";
|
||||
font-family: 'Roboto';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: local("Roboto"), local("Roboto-Regular"),
|
||||
url(../fonts/roboto/CWB0XYA8bzo0kSThX0UTuA.woff2) format("woff2");
|
||||
unicode-range: U+0000-00ff, U+0131, U+0152-0153, U+02c6, U+02da, U+02dc,
|
||||
U+2000-206f, U+2074, U+20ac, U+2212, U+2215;
|
||||
src: local('Roboto'), local('Roboto-Regular'), url(../fonts/roboto/CWB0XYA8bzo0kSThX0UTuA.woff2) format('woff2');
|
||||
unicode-range: U+0000-00ff, U+0131, U+0152-0153, U+02c6, U+02da, U+02dc, U+2000-206f, U+2074, U+20ac, U+2212, U+2215;
|
||||
}
|
||||
/* cyrillic-ext */
|
||||
@font-face {
|
||||
font-family: "Roboto";
|
||||
font-family: 'Roboto';
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
src: local("Roboto Medium"), local("Roboto-Medium"),
|
||||
url(../fonts/roboto/ZLqKeelYbATG60EpZBSDyxJtnKITppOI_IvcXXDNrsc.woff2)
|
||||
format("woff2");
|
||||
src: local('Roboto Medium'), local('Roboto-Medium'),
|
||||
url(../fonts/roboto/ZLqKeelYbATG60EpZBSDyxJtnKITppOI_IvcXXDNrsc.woff2) format('woff2');
|
||||
unicode-range: U+0460-052f, U+20b4, U+2de0-2dff, U+A640-A69F;
|
||||
}
|
||||
/* cyrillic */
|
||||
@font-face {
|
||||
font-family: "Roboto";
|
||||
font-family: 'Roboto';
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
src: local("Roboto Medium"), local("Roboto-Medium"),
|
||||
url(../fonts/roboto/oHi30kwQWvpCWqAhzHcCSBJtnKITppOI_IvcXXDNrsc.woff2)
|
||||
format("woff2");
|
||||
src: local('Roboto Medium'), local('Roboto-Medium'),
|
||||
url(../fonts/roboto/oHi30kwQWvpCWqAhzHcCSBJtnKITppOI_IvcXXDNrsc.woff2) format('woff2');
|
||||
unicode-range: U+0400-045f, U+0490-0491, U+04b0-04b1, U+2116;
|
||||
}
|
||||
/* greek-ext */
|
||||
@font-face {
|
||||
font-family: "Roboto";
|
||||
font-family: 'Roboto';
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
src: local("Roboto Medium"), local("Roboto-Medium"),
|
||||
url(../fonts/roboto/rGvHdJnr2l75qb0YND9NyBJtnKITppOI_IvcXXDNrsc.woff2)
|
||||
format("woff2");
|
||||
src: local('Roboto Medium'), local('Roboto-Medium'),
|
||||
url(../fonts/roboto/rGvHdJnr2l75qb0YND9NyBJtnKITppOI_IvcXXDNrsc.woff2) format('woff2');
|
||||
unicode-range: U+1f00-1fff;
|
||||
}
|
||||
/* greek */
|
||||
@font-face {
|
||||
font-family: "Roboto";
|
||||
font-family: 'Roboto';
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
src: local("Roboto Medium"), local("Roboto-Medium"),
|
||||
url(../fonts/roboto/mx9Uck6uB63VIKFYnEMXrRJtnKITppOI_IvcXXDNrsc.woff2)
|
||||
format("woff2");
|
||||
src: local('Roboto Medium'), local('Roboto-Medium'),
|
||||
url(../fonts/roboto/mx9Uck6uB63VIKFYnEMXrRJtnKITppOI_IvcXXDNrsc.woff2) format('woff2');
|
||||
unicode-range: U+0370-03ff;
|
||||
}
|
||||
/* vietnamese */
|
||||
@font-face {
|
||||
font-family: "Roboto";
|
||||
font-family: 'Roboto';
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
src: local("Roboto Medium"), local("Roboto-Medium"),
|
||||
url(../fonts/roboto/mbmhprMH69Zi6eEPBYVFhRJtnKITppOI_IvcXXDNrsc.woff2)
|
||||
format("woff2");
|
||||
src: local('Roboto Medium'), local('Roboto-Medium'),
|
||||
url(../fonts/roboto/mbmhprMH69Zi6eEPBYVFhRJtnKITppOI_IvcXXDNrsc.woff2) format('woff2');
|
||||
unicode-range: U+0102-0103, U+1ea0-1ef9, U+20ab;
|
||||
}
|
||||
/* latin-ext */
|
||||
@font-face {
|
||||
font-family: "Roboto";
|
||||
font-family: 'Roboto';
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
src: local("Roboto Medium"), local("Roboto-Medium"),
|
||||
url(../fonts/roboto/oOeFwZNlrTefzLYmlVV1UBJtnKITppOI_IvcXXDNrsc.woff2)
|
||||
format("woff2");
|
||||
unicode-range: U+0100-024f, U+1-1eff, U+20a0-20ab, U+20ad-20cf, U+2c60-2c7f,
|
||||
U+A720-A7FF;
|
||||
src: local('Roboto Medium'), local('Roboto-Medium'),
|
||||
url(../fonts/roboto/oOeFwZNlrTefzLYmlVV1UBJtnKITppOI_IvcXXDNrsc.woff2) format('woff2');
|
||||
unicode-range: U+0100-024f, U+1-1eff, U+20a0-20ab, U+20ad-20cf, U+2c60-2c7f, U+A720-A7FF;
|
||||
}
|
||||
/* latin */
|
||||
@font-face {
|
||||
font-family: "Roboto";
|
||||
font-family: 'Roboto';
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
src: local("Roboto Medium"), local("Roboto-Medium"),
|
||||
url(../fonts/roboto/RxZJdnzeo3R5zSexge8UUVtXRa8TVwTICgirnJhmVJw.woff2)
|
||||
format("woff2");
|
||||
unicode-range: U+0000-00ff, U+0131, U+0152-0153, U+02c6, U+02da, U+02dc,
|
||||
U+2000-206f, U+2074, U+20ac, U+2212, U+2215;
|
||||
src: local('Roboto Medium'), local('Roboto-Medium'),
|
||||
url(../fonts/roboto/RxZJdnzeo3R5zSexge8UUVtXRa8TVwTICgirnJhmVJw.woff2) format('woff2');
|
||||
unicode-range: U+0000-00ff, U+0131, U+0152-0153, U+02c6, U+02da, U+02dc, U+2000-206f, U+2074, U+20ac, U+2212, U+2215;
|
||||
}
|
||||
/* cyrillic-ext */
|
||||
@font-face {
|
||||
font-family: "Roboto";
|
||||
font-family: 'Roboto';
|
||||
font-style: italic;
|
||||
font-weight: 400;
|
||||
src: local("Roboto Italic"), local("Roboto-Italic"),
|
||||
url(../fonts/roboto/WxrXJa0C3KdtC7lMafG4dRTbgVql8nDJpwnrE27mub0.woff2)
|
||||
format("woff2");
|
||||
src: local('Roboto Italic'), local('Roboto-Italic'),
|
||||
url(../fonts/roboto/WxrXJa0C3KdtC7lMafG4dRTbgVql8nDJpwnrE27mub0.woff2) format('woff2');
|
||||
unicode-range: U+0460-052f, U+20b4, U+2de0-2dff, U+A640-A69F;
|
||||
}
|
||||
/* cyrillic */
|
||||
@font-face {
|
||||
font-family: "Roboto";
|
||||
font-family: 'Roboto';
|
||||
font-style: italic;
|
||||
font-weight: 400;
|
||||
src: local("Roboto Italic"), local("Roboto-Italic"),
|
||||
url(../fonts/roboto/OpXUqTo0UgQQhGj_SFdLWBTbgVql8nDJpwnrE27mub0.woff2)
|
||||
format("woff2");
|
||||
src: local('Roboto Italic'), local('Roboto-Italic'),
|
||||
url(../fonts/roboto/OpXUqTo0UgQQhGj_SFdLWBTbgVql8nDJpwnrE27mub0.woff2) format('woff2');
|
||||
unicode-range: U+0400-045f, U+0490-0491, U+04b0-04b1, U+2116;
|
||||
}
|
||||
/* greek-ext */
|
||||
@font-face {
|
||||
font-family: "Roboto";
|
||||
font-family: 'Roboto';
|
||||
font-style: italic;
|
||||
font-weight: 400;
|
||||
src: local("Roboto Italic"), local("Roboto-Italic"),
|
||||
url(../fonts/roboto/1hZf02POANh32k2VkgEoUBTbgVql8nDJpwnrE27mub0.woff2)
|
||||
format("woff2");
|
||||
src: local('Roboto Italic'), local('Roboto-Italic'),
|
||||
url(../fonts/roboto/1hZf02POANh32k2VkgEoUBTbgVql8nDJpwnrE27mub0.woff2) format('woff2');
|
||||
unicode-range: U+1f00-1fff;
|
||||
}
|
||||
/* greek */
|
||||
@font-face {
|
||||
font-family: "Roboto";
|
||||
font-family: 'Roboto';
|
||||
font-style: italic;
|
||||
font-weight: 400;
|
||||
src: local("Roboto Italic"), local("Roboto-Italic"),
|
||||
url(../fonts/roboto/cDKhRaXnQTOVbaoxwdOr9xTbgVql8nDJpwnrE27mub0.woff2)
|
||||
format("woff2");
|
||||
src: local('Roboto Italic'), local('Roboto-Italic'),
|
||||
url(../fonts/roboto/cDKhRaXnQTOVbaoxwdOr9xTbgVql8nDJpwnrE27mub0.woff2) format('woff2');
|
||||
unicode-range: U+0370-03ff;
|
||||
}
|
||||
/* vietnamese */
|
||||
@font-face {
|
||||
font-family: "Roboto";
|
||||
font-family: 'Roboto';
|
||||
font-style: italic;
|
||||
font-weight: 400;
|
||||
src: local("Roboto Italic"), local("Roboto-Italic"),
|
||||
url(../fonts/roboto/K23cxWVTrIFD6DJsEVi07RTbgVql8nDJpwnrE27mub0.woff2)
|
||||
format("woff2");
|
||||
src: local('Roboto Italic'), local('Roboto-Italic'),
|
||||
url(../fonts/roboto/K23cxWVTrIFD6DJsEVi07RTbgVql8nDJpwnrE27mub0.woff2) format('woff2');
|
||||
unicode-range: U+0102-0103, U+1ea0-1ef9, U+20ab;
|
||||
}
|
||||
/* latin-ext */
|
||||
@font-face {
|
||||
font-family: "Roboto";
|
||||
font-family: 'Roboto';
|
||||
font-style: italic;
|
||||
font-weight: 400;
|
||||
src: local("Roboto Italic"), local("Roboto-Italic"),
|
||||
url(../fonts/roboto/vSzulfKSK0LLjjfeaxcREhTbgVql8nDJpwnrE27mub0.woff2)
|
||||
format("woff2");
|
||||
unicode-range: U+0100-024f, U+1-1eff, U+20a0-20ab, U+20ad-20cf, U+2c60-2c7f,
|
||||
U+A720-A7FF;
|
||||
src: local('Roboto Italic'), local('Roboto-Italic'),
|
||||
url(../fonts/roboto/vSzulfKSK0LLjjfeaxcREhTbgVql8nDJpwnrE27mub0.woff2) format('woff2');
|
||||
unicode-range: U+0100-024f, U+1-1eff, U+20a0-20ab, U+20ad-20cf, U+2c60-2c7f, U+A720-A7FF;
|
||||
}
|
||||
/* latin */
|
||||
@font-face {
|
||||
font-family: "Roboto";
|
||||
font-family: 'Roboto';
|
||||
font-style: italic;
|
||||
font-weight: 400;
|
||||
src: local("Roboto Italic"), local("Roboto-Italic"),
|
||||
url(../fonts/roboto/vPcynSL0qHq_6dX7lKVByfesZW2xOQ-xsNqO47m55DA.woff2)
|
||||
format("woff2");
|
||||
unicode-range: U+0000-00ff, U+0131, U+0152-0153, U+02c6, U+02da, U+02dc,
|
||||
U+2000-206f, U+2074, U+20ac, U+2212, U+2215;
|
||||
src: local('Roboto Italic'), local('Roboto-Italic'),
|
||||
url(../fonts/roboto/vPcynSL0qHq_6dX7lKVByfesZW2xOQ-xsNqO47m55DA.woff2) format('woff2');
|
||||
unicode-range: U+0000-00ff, U+0131, U+0152-0153, U+02c6, U+02da, U+02dc, U+2000-206f, U+2074, U+20ac, U+2212, U+2215;
|
||||
}
|
||||
/* cyrillic-ext */
|
||||
@font-face {
|
||||
font-family: "Roboto";
|
||||
font-family: 'Roboto';
|
||||
font-style: italic;
|
||||
font-weight: 500;
|
||||
src: local("Roboto Medium Italic"), local("Roboto-MediumItalic"),
|
||||
url(../fonts/roboto/OLffGBTaF0XFOW1gnuHF0TTOQ_MqJVwkKsUn0wKzc2I.woff2)
|
||||
format("woff2");
|
||||
src: local('Roboto Medium Italic'), local('Roboto-MediumItalic'),
|
||||
url(../fonts/roboto/OLffGBTaF0XFOW1gnuHF0TTOQ_MqJVwkKsUn0wKzc2I.woff2) format('woff2');
|
||||
unicode-range: U+0460-052f, U+20b4, U+2de0-2dff, U+A640-A69F;
|
||||
}
|
||||
/* cyrillic */
|
||||
@font-face {
|
||||
font-family: "Roboto";
|
||||
font-family: 'Roboto';
|
||||
font-style: italic;
|
||||
font-weight: 500;
|
||||
src: local("Roboto Medium Italic"), local("Roboto-MediumItalic"),
|
||||
url(../fonts/roboto/OLffGBTaF0XFOW1gnuHF0TUj_cnvWIuuBMVgbX098Mw.woff2)
|
||||
format("woff2");
|
||||
src: local('Roboto Medium Italic'), local('Roboto-MediumItalic'),
|
||||
url(../fonts/roboto/OLffGBTaF0XFOW1gnuHF0TUj_cnvWIuuBMVgbX098Mw.woff2) format('woff2');
|
||||
unicode-range: U+0400-045f, U+0490-0491, U+04b0-04b1, U+2116;
|
||||
}
|
||||
/* greek-ext */
|
||||
@font-face {
|
||||
font-family: "Roboto";
|
||||
font-family: 'Roboto';
|
||||
font-style: italic;
|
||||
font-weight: 500;
|
||||
src: local("Roboto Medium Italic"), local("Roboto-MediumItalic"),
|
||||
url(../fonts/roboto/OLffGBTaF0XFOW1gnuHF0UbcKLIaa1LC45dFaAfauRA.woff2)
|
||||
format("woff2");
|
||||
src: local('Roboto Medium Italic'), local('Roboto-MediumItalic'),
|
||||
url(../fonts/roboto/OLffGBTaF0XFOW1gnuHF0UbcKLIaa1LC45dFaAfauRA.woff2) format('woff2');
|
||||
unicode-range: U+1f00-1fff;
|
||||
}
|
||||
/* greek */
|
||||
@font-face {
|
||||
font-family: "Roboto";
|
||||
font-family: 'Roboto';
|
||||
font-style: italic;
|
||||
font-weight: 500;
|
||||
src: local("Roboto Medium Italic"), local("Roboto-MediumItalic"),
|
||||
url(../fonts/roboto/OLffGBTaF0XFOW1gnuHF0Wo_sUJ8uO4YLWRInS22T3Y.woff2)
|
||||
format("woff2");
|
||||
src: local('Roboto Medium Italic'), local('Roboto-MediumItalic'),
|
||||
url(../fonts/roboto/OLffGBTaF0XFOW1gnuHF0Wo_sUJ8uO4YLWRInS22T3Y.woff2) format('woff2');
|
||||
unicode-range: U+0370-03ff;
|
||||
}
|
||||
/* vietnamese */
|
||||
@font-face {
|
||||
font-family: "Roboto";
|
||||
font-family: 'Roboto';
|
||||
font-style: italic;
|
||||
font-weight: 500;
|
||||
src: local("Roboto Medium Italic"), local("Roboto-MediumItalic"),
|
||||
url(../fonts/roboto/OLffGBTaF0XFOW1gnuHF0b6up8jxqWt8HVA3mDhkV_0.woff2)
|
||||
format("woff2");
|
||||
src: local('Roboto Medium Italic'), local('Roboto-MediumItalic'),
|
||||
url(../fonts/roboto/OLffGBTaF0XFOW1gnuHF0b6up8jxqWt8HVA3mDhkV_0.woff2) format('woff2');
|
||||
unicode-range: U+0102-0103, U+1ea0-1ef9, U+20ab;
|
||||
}
|
||||
/* latin-ext */
|
||||
@font-face {
|
||||
font-family: "Roboto";
|
||||
font-family: 'Roboto';
|
||||
font-style: italic;
|
||||
font-weight: 500;
|
||||
src: local("Roboto Medium Italic"), local("Roboto-MediumItalic"),
|
||||
url(../fonts/roboto/OLffGBTaF0XFOW1gnuHF0SYE0-AqJ3nfInTTiDXDjU4.woff2)
|
||||
format("woff2");
|
||||
unicode-range: U+0100-024f, U+1-1eff, U+20a0-20ab, U+20ad-20cf, U+2c60-2c7f,
|
||||
U+A720-A7FF;
|
||||
src: local('Roboto Medium Italic'), local('Roboto-MediumItalic'),
|
||||
url(../fonts/roboto/OLffGBTaF0XFOW1gnuHF0SYE0-AqJ3nfInTTiDXDjU4.woff2) format('woff2');
|
||||
unicode-range: U+0100-024f, U+1-1eff, U+20a0-20ab, U+20ad-20cf, U+2c60-2c7f, U+A720-A7FF;
|
||||
}
|
||||
/* latin */
|
||||
@font-face {
|
||||
font-family: "Roboto";
|
||||
font-family: 'Roboto';
|
||||
font-style: italic;
|
||||
font-weight: 500;
|
||||
src: local("Roboto Medium Italic"), local("Roboto-MediumItalic"),
|
||||
url(../fonts/roboto/OLffGBTaF0XFOW1gnuHF0Y4P5ICox8Kq3LLUNMylGO4.woff2)
|
||||
format("woff2");
|
||||
unicode-range: U+0000-00ff, U+0131, U+0152-0153, U+02c6, U+02da, U+02dc,
|
||||
U+2000-206f, U+2074, U+20ac, U+2212, U+2215;
|
||||
src: local('Roboto Medium Italic'), local('Roboto-MediumItalic'),
|
||||
url(../fonts/roboto/OLffGBTaF0XFOW1gnuHF0Y4P5ICox8Kq3LLUNMylGO4.woff2) format('woff2');
|
||||
unicode-range: U+0000-00ff, U+0131, U+0152-0153, U+02c6, U+02da, U+02dc, U+2000-206f, U+2074, U+20ac, U+2212, U+2215;
|
||||
}
|
||||
|
||||
@@ -41,8 +41,8 @@
|
||||
|
||||
.theme-dark {
|
||||
.react-grid-item > .react-resizable-handle::after {
|
||||
border-right: 2px solid $gray-4;
|
||||
border-bottom: 2px solid $gray-4;
|
||||
border-right: 2px solid $gray-1;
|
||||
border-bottom: 2px solid $gray-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
|
||||
|
||||
@@ -16,6 +16,10 @@
|
||||
padding-left: 0px;
|
||||
}
|
||||
|
||||
.graph-legend-table {
|
||||
width: auto;
|
||||
}
|
||||
|
||||
.graph-legend-table .graph-legend-series {
|
||||
display: table-row;
|
||||
}
|
||||
@@ -45,7 +49,7 @@
|
||||
.graph-legend {
|
||||
flex: 0 1 auto;
|
||||
max-height: 30%;
|
||||
margin: 0 $spacer;
|
||||
margin: 0;
|
||||
text-align: center;
|
||||
padding-top: 6px;
|
||||
position: relative;
|
||||
@@ -70,19 +74,19 @@
|
||||
font-size: 85%;
|
||||
text-align: left;
|
||||
&.current::before {
|
||||
content: "Current: ";
|
||||
content: 'Current: ';
|
||||
}
|
||||
&.max::before {
|
||||
content: "Max: ";
|
||||
content: 'Max: ';
|
||||
}
|
||||
&.min::before {
|
||||
content: "Min: ";
|
||||
content: 'Min: ';
|
||||
}
|
||||
&.total::before {
|
||||
content: "Total: ";
|
||||
content: 'Total: ';
|
||||
}
|
||||
&.avg::before {
|
||||
content: "Avg: ";
|
||||
content: 'Avg: ';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -106,6 +110,15 @@
|
||||
padding-left: 6px;
|
||||
}
|
||||
|
||||
// fix for phantomjs
|
||||
.body--phantomjs {
|
||||
.graph-panel--legend-right {
|
||||
.graph-legend-table {
|
||||
display: table;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.graph-legend-table {
|
||||
tbody {
|
||||
display: block;
|
||||
@@ -114,6 +127,7 @@
|
||||
height: 100%;
|
||||
padding-bottom: 1px;
|
||||
padding-right: 5px;
|
||||
padding-left: 5px;
|
||||
}
|
||||
|
||||
.graph-legend-series {
|
||||
@@ -124,7 +138,7 @@
|
||||
float: none;
|
||||
|
||||
.graph-legend-alias::after {
|
||||
content: "(right-y)";
|
||||
content: '(right-y)';
|
||||
padding: 0 5px;
|
||||
color: $text-color-weak;
|
||||
}
|
||||
@@ -175,7 +189,7 @@
|
||||
&.total,
|
||||
&.avg {
|
||||
&::before {
|
||||
content: "";
|
||||
content: '';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user