mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Merge branch 'master' into redux-poc2
This commit is contained in:
@@ -21,7 +21,7 @@ import _ from 'lodash';
|
||||
import moment from 'moment';
|
||||
|
||||
// add move to lodash for backward compatabiltiy
|
||||
_.move = function(array, fromIndex, toIndex) {
|
||||
_.move = (array, fromIndex, toIndex) => {
|
||||
array.splice(toIndex, 0, array.splice(fromIndex, 1)[0]);
|
||||
return array;
|
||||
};
|
||||
@@ -76,9 +76,9 @@ export class GrafanaApp {
|
||||
$provide.decorator('$http', [
|
||||
'$delegate',
|
||||
'$templateCache',
|
||||
function($delegate, $templateCache) {
|
||||
($delegate, $templateCache) => {
|
||||
const get = $delegate.get;
|
||||
$delegate.get = function(url, config) {
|
||||
$delegate.get = (url, config) => {
|
||||
if (url.match(/\.html$/)) {
|
||||
// some template's already exist in the cache
|
||||
if (!$templateCache.get(url)) {
|
||||
@@ -105,9 +105,9 @@ export class GrafanaApp {
|
||||
'react',
|
||||
];
|
||||
|
||||
const module_types = ['controllers', 'directives', 'factories', 'services', 'filters', 'routes'];
|
||||
const moduleTypes = ['controllers', 'directives', 'factories', 'services', 'filters', 'routes'];
|
||||
|
||||
_.each(module_types, type => {
|
||||
_.each(moduleTypes, type => {
|
||||
const moduleName = 'grafana.' + type;
|
||||
this.useModule(angular.module(moduleName, []));
|
||||
});
|
||||
@@ -135,7 +135,7 @@ export class GrafanaApp {
|
||||
this.preBootModules = null;
|
||||
});
|
||||
})
|
||||
.catch(function(err) {
|
||||
.catch(err => {
|
||||
console.log('Application boot failed:', err);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -173,6 +173,12 @@ export class Explore extends React.Component<any, ExploreState> {
|
||||
datasource.init();
|
||||
}
|
||||
|
||||
// Keep queries but reset edit state
|
||||
const nextQueries = this.state.queries.map(q => ({
|
||||
...q,
|
||||
edited: false,
|
||||
}));
|
||||
|
||||
this.setState(
|
||||
{
|
||||
datasource,
|
||||
@@ -182,6 +188,7 @@ export class Explore extends React.Component<any, ExploreState> {
|
||||
supportsLogs,
|
||||
supportsTable,
|
||||
datasourceLoading: false,
|
||||
queries: nextQueries,
|
||||
},
|
||||
() => datasourceError === null && this.onSubmit()
|
||||
);
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
// Based on underscore.js debounce()
|
||||
export default function debounce(func, wait) {
|
||||
let timeout;
|
||||
return function() {
|
||||
return function(this: any) {
|
||||
const context = this;
|
||||
const args = arguments;
|
||||
const later = function() {
|
||||
const later = () => {
|
||||
timeout = null;
|
||||
func.apply(context, args);
|
||||
};
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// Node.closest() polyfill
|
||||
if ('Element' in window && !Element.prototype.closest) {
|
||||
Element.prototype.closest = function(s) {
|
||||
Element.prototype.closest = function(this: any, s) {
|
||||
const matches = (this.document || this.ownerDocument).querySelectorAll(s);
|
||||
let el = this;
|
||||
let i;
|
||||
@@ -9,7 +9,8 @@ if ('Element' in window && !Element.prototype.closest) {
|
||||
i = matches.length;
|
||||
// eslint-disable-next-line
|
||||
while (--i >= 0 && matches.item(i) !== el) {}
|
||||
} while (i < 0 && (el = el.parentElement));
|
||||
el = el.parentElement;
|
||||
} while (i < 0 && el);
|
||||
return el;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import { NavStore } from 'app/stores/NavStore/NavStore';
|
||||
import { TeamsStore, Team } from 'app/stores/TeamsStore/TeamsStore';
|
||||
import { BackendSrv } from 'app/core/services/backend_srv';
|
||||
import DeleteButton from 'app/core/components/DeleteButton/DeleteButton';
|
||||
import EmptyListCTA from 'app/core/components/EmptyListCTA/EmptyListCTA';
|
||||
|
||||
interface Props {
|
||||
nav: typeof NavStore.Type;
|
||||
@@ -61,48 +62,81 @@ export class TeamList extends React.Component<Props, any> {
|
||||
);
|
||||
}
|
||||
|
||||
renderTeamList(teams) {
|
||||
return (
|
||||
<div className="page-container page-body">
|
||||
<div className="page-action-bar">
|
||||
<div className="gf-form gf-form--grow">
|
||||
<label className="gf-form--has-input-icon gf-form--grow">
|
||||
<input
|
||||
type="text"
|
||||
className="gf-form-input"
|
||||
placeholder="Search teams"
|
||||
value={teams.search}
|
||||
onChange={this.onSearchQueryChange}
|
||||
/>
|
||||
<i className="gf-form-input-icon fa fa-search" />
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div className="page-action-bar__spacer" />
|
||||
|
||||
<a className="btn btn-success" href="org/teams/new">
|
||||
<i className="fa fa-plus" /> New team
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div className="admin-list-table">
|
||||
<table className="filter-table filter-table--hover form-inline">
|
||||
<thead>
|
||||
<tr>
|
||||
<th />
|
||||
<th>Name</th>
|
||||
<th>Email</th>
|
||||
<th>Members</th>
|
||||
<th style={{ width: '1%' }} />
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>{teams.filteredTeams.map(team => this.renderTeamMember(team))}</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
renderEmptyList() {
|
||||
return (
|
||||
<div className="page-container page-body">
|
||||
<EmptyListCTA
|
||||
model={{
|
||||
title: "You haven't created any teams yet.",
|
||||
buttonIcon: 'fa fa-plus',
|
||||
buttonLink: 'org/teams/new',
|
||||
buttonTitle: ' New team',
|
||||
proTip: 'Assign folder and dashboard permissions to teams instead of users to ease administration.',
|
||||
proTipLink: '',
|
||||
proTipLinkTitle: '',
|
||||
proTipTarget: '_blank',
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
const { nav, teams } = this.props;
|
||||
let view;
|
||||
|
||||
if (teams.filteredTeams.length > 0) {
|
||||
view = this.renderTeamList(teams);
|
||||
} else {
|
||||
view = this.renderEmptyList();
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<PageHeader model={nav as any} />
|
||||
<div className="page-container page-body">
|
||||
<div className="page-action-bar">
|
||||
<div className="gf-form gf-form--grow">
|
||||
<label className="gf-form--has-input-icon gf-form--grow">
|
||||
<input
|
||||
type="text"
|
||||
className="gf-form-input"
|
||||
placeholder="Search teams"
|
||||
value={teams.search}
|
||||
onChange={this.onSearchQueryChange}
|
||||
/>
|
||||
<i className="gf-form-input-icon fa fa-search" />
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div className="page-action-bar__spacer" />
|
||||
|
||||
<a className="btn btn-success" href="org/teams/new">
|
||||
<i className="fa fa-plus" /> New team
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div className="admin-list-table">
|
||||
<table className="filter-table filter-table--hover form-inline">
|
||||
<thead>
|
||||
<tr>
|
||||
<th />
|
||||
<th>Name</th>
|
||||
<th>Email</th>
|
||||
<th>Members</th>
|
||||
<th style={{ width: '1%' }} />
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>{teams.filteredTeams.map(team => this.renderTeamMember(team))}</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
{view}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -2,16 +2,16 @@ import { react2AngularDirective } from 'app/core/utils/react2angular';
|
||||
import { PasswordStrength } from './components/PasswordStrength';
|
||||
import PageHeader from './components/PageHeader/PageHeader';
|
||||
import EmptyListCTA from './components/EmptyListCTA/EmptyListCTA';
|
||||
import LoginBackground from './components/Login/LoginBackground';
|
||||
import { SearchResult } from './components/search/SearchResult';
|
||||
import { TagFilter } from './components/TagFilter/TagFilter';
|
||||
import { SideMenu } from './components/sidemenu/SideMenu';
|
||||
import DashboardPermissions from './components/Permissions/DashboardPermissions';
|
||||
|
||||
export function registerAngularDirectives() {
|
||||
react2AngularDirective('passwordStrength', PasswordStrength, ['password']);
|
||||
react2AngularDirective('sidemenu', SideMenu, []);
|
||||
react2AngularDirective('pageHeader', PageHeader, ['model', 'noTabs']);
|
||||
react2AngularDirective('emptyListCta', EmptyListCTA, ['model']);
|
||||
react2AngularDirective('loginBackground', LoginBackground, []);
|
||||
react2AngularDirective('searchResult', SearchResult, []);
|
||||
react2AngularDirective('tagFilter', TagFilter, [
|
||||
'tags',
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Emitter } from './utils/emitter';
|
||||
|
||||
var appEvents = new Emitter();
|
||||
const appEvents = new Emitter();
|
||||
export default appEvents;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -84,7 +84,7 @@ function link(scope, elem, attrs) {
|
||||
// disable depreacation warning
|
||||
codeEditor.$blockScrolling = Infinity;
|
||||
// Padding hacks
|
||||
(<any>codeEditor.renderer).setScrollMargin(15, 15);
|
||||
(codeEditor.renderer as any).setScrollMargin(15, 15);
|
||||
codeEditor.renderer.setPadding(10);
|
||||
|
||||
setThemeMode();
|
||||
@@ -97,11 +97,11 @@ function link(scope, elem, attrs) {
|
||||
textarea.addClass('gf-form-input');
|
||||
|
||||
if (scope.codeEditorFocus) {
|
||||
setTimeout(function() {
|
||||
setTimeout(() => {
|
||||
textarea.focus();
|
||||
var domEl = textarea[0];
|
||||
const domEl = textarea[0];
|
||||
if (domEl.setSelectionRange) {
|
||||
var pos = textarea.val().length * 2;
|
||||
const pos = textarea.val().length * 2;
|
||||
domEl.setSelectionRange(pos, pos);
|
||||
}
|
||||
}, 100);
|
||||
@@ -119,7 +119,7 @@ function link(scope, elem, attrs) {
|
||||
scope.$watch('content', (newValue, oldValue) => {
|
||||
const editorValue = codeEditor.getValue();
|
||||
if (newValue !== editorValue && newValue !== oldValue) {
|
||||
scope.$$postDigest(function() {
|
||||
scope.$$postDigest(() => {
|
||||
setEditorContent(newValue);
|
||||
});
|
||||
}
|
||||
@@ -152,7 +152,7 @@ function link(scope, elem, attrs) {
|
||||
|
||||
if (scope.getCompleter()) {
|
||||
// make copy of array as ace seems to share completers array between instances
|
||||
const anyEditor = <any>codeEditor;
|
||||
const anyEditor = codeEditor as any;
|
||||
anyEditor.completers = anyEditor.completers.slice();
|
||||
anyEditor.completers.push(scope.getCompleter());
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ export function spectrumPicker() {
|
||||
scope: true,
|
||||
replace: true,
|
||||
template: '<color-picker color="ngModel.$viewValue" onChange="onColorChange"></color-picker>',
|
||||
link: function(scope, element, attrs, ngModel) {
|
||||
link: (scope, element, attrs, ngModel) => {
|
||||
scope.ngModel = ngModel;
|
||||
scope.onColorChange = color => {
|
||||
ngModel.$setViewValue(color);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import coreModule from 'app/core/core_module';
|
||||
|
||||
var template = `
|
||||
const template = `
|
||||
<select class="gf-form-input" ng-model="ctrl.model" ng-options="f.value as f.text for f in ctrl.options"></select>
|
||||
`;
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import _ from 'lodash';
|
||||
import coreModule from '../../core_module';
|
||||
|
||||
function typeaheadMatcher(item) {
|
||||
var str = this.query;
|
||||
function typeaheadMatcher(this: any, item) {
|
||||
let str = this.query;
|
||||
if (str === '') {
|
||||
return true;
|
||||
}
|
||||
@@ -36,7 +36,7 @@ export class FormDropdownCtrl {
|
||||
startOpen: any;
|
||||
debounce: number;
|
||||
|
||||
/** @ngInject **/
|
||||
/** @ngInject */
|
||||
constructor(private $scope, $element, private $sce, private templateSrv, private $q) {
|
||||
this.inputElement = $element.find('input').first();
|
||||
this.linkElement = $element.find('a').first();
|
||||
|
||||
@@ -31,7 +31,7 @@ export function gfPageDirective() {
|
||||
header: '?gfPageHeader',
|
||||
body: 'gfPageBody',
|
||||
},
|
||||
link: function(scope, elem, attrs) {
|
||||
link: (scope, elem, attrs) => {
|
||||
console.log(scope);
|
||||
},
|
||||
};
|
||||
|
||||
@@ -30,7 +30,7 @@ export class GrafanaCtrl {
|
||||
setBackendSrv(backendSrv);
|
||||
createStore({ backendSrv, datasourceSrv });
|
||||
|
||||
$scope.init = function() {
|
||||
$scope.init = () => {
|
||||
$scope.contextSrv = contextSrv;
|
||||
$scope.appSubUrl = config.appSubUrl;
|
||||
$scope._ = _;
|
||||
@@ -45,14 +45,14 @@ export class GrafanaCtrl {
|
||||
|
||||
$rootScope.colors = colors;
|
||||
|
||||
$scope.initDashboard = function(dashboardData, viewScope) {
|
||||
$scope.initDashboard = (dashboardData, viewScope) => {
|
||||
$scope.appEvent('dashboard-fetch-end', dashboardData);
|
||||
$controller('DashboardCtrl', { $scope: viewScope }).init(dashboardData);
|
||||
};
|
||||
|
||||
$rootScope.onAppEvent = function(name, callback, localScope) {
|
||||
var unbind = $rootScope.$on(name, callback);
|
||||
var callerScope = this;
|
||||
const unbind = $rootScope.$on(name, callback);
|
||||
let callerScope = this;
|
||||
if (callerScope.$id === 1 && !localScope) {
|
||||
console.log('warning rootScope onAppEvent called without localscope');
|
||||
}
|
||||
@@ -62,7 +62,7 @@ export class GrafanaCtrl {
|
||||
callerScope.$on('$destroy', unbind);
|
||||
};
|
||||
|
||||
$rootScope.appEvent = function(name, payload) {
|
||||
$rootScope.appEvent = (name, payload) => {
|
||||
$rootScope.$emit(name, payload);
|
||||
appEvents.emit(name, payload);
|
||||
};
|
||||
@@ -71,17 +71,43 @@ export class GrafanaCtrl {
|
||||
}
|
||||
}
|
||||
|
||||
function setViewModeBodyClass(body, mode, sidemenuOpen: boolean) {
|
||||
body.removeClass('view-mode--tv');
|
||||
body.removeClass('view-mode--kiosk');
|
||||
body.removeClass('view-mode--inactive');
|
||||
|
||||
switch (mode) {
|
||||
case 'tv': {
|
||||
body.removeClass('sidemenu-open');
|
||||
body.addClass('view-mode--tv');
|
||||
break;
|
||||
}
|
||||
// 1 & true for legacy states
|
||||
case 1:
|
||||
case true: {
|
||||
body.removeClass('sidemenu-open');
|
||||
body.addClass('view-mode--kiosk');
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
body.toggleClass('sidemenu-open', sidemenuOpen);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** @ngInject */
|
||||
export function grafanaAppDirective(playlistSrv, contextSrv, $timeout, $rootScope, $location) {
|
||||
return {
|
||||
restrict: 'E',
|
||||
controller: GrafanaCtrl,
|
||||
link: (scope, elem) => {
|
||||
var sidemenuOpen;
|
||||
var body = $('body');
|
||||
let sidemenuOpen;
|
||||
const body = $('body');
|
||||
|
||||
// see https://github.com/zenorocha/clipboard.js/issues/155
|
||||
$.fn.modal.Constructor.prototype.enforceFocus = function() {};
|
||||
$.fn.modal.Constructor.prototype.enforceFocus = () => {};
|
||||
|
||||
$('.preloader').remove();
|
||||
|
||||
sidemenuOpen = scope.contextSrv.sidemenu;
|
||||
body.toggleClass('sidemenu-open', sidemenuOpen);
|
||||
@@ -99,9 +125,12 @@ export function grafanaAppDirective(playlistSrv, contextSrv, $timeout, $rootScop
|
||||
body.toggleClass('sidemenu-hidden');
|
||||
});
|
||||
|
||||
scope.$watch(() => playlistSrv.isPlaying, function(newValue) {
|
||||
elem.toggleClass('playlist-active', newValue === true);
|
||||
});
|
||||
scope.$watch(
|
||||
() => playlistSrv.isPlaying,
|
||||
newValue => {
|
||||
elem.toggleClass('view-mode--playlist', newValue === true);
|
||||
}
|
||||
);
|
||||
|
||||
// check if we are in server side render
|
||||
if (document.cookie.indexOf('renderKey') !== -1) {
|
||||
@@ -110,8 +139,8 @@ export function grafanaAppDirective(playlistSrv, contextSrv, $timeout, $rootScop
|
||||
|
||||
// tooltip removal fix
|
||||
// manage page classes
|
||||
var pageClass;
|
||||
scope.$on('$routeChangeSuccess', function(evt, data) {
|
||||
let pageClass;
|
||||
scope.$on('$routeChangeSuccess', (evt, data) => {
|
||||
if (pageClass) {
|
||||
body.removeClass(pageClass);
|
||||
}
|
||||
@@ -129,17 +158,7 @@ export function grafanaAppDirective(playlistSrv, contextSrv, $timeout, $rootScop
|
||||
$('#tooltip, .tooltip').remove();
|
||||
|
||||
// check for kiosk url param
|
||||
if (data.params.kiosk) {
|
||||
appEvents.emit('toggle-kiosk-mode');
|
||||
}
|
||||
|
||||
// check for 'inactive' url param for clean looks like kiosk, but with title
|
||||
if (data.params.inactive) {
|
||||
body.addClass('user-activity-low');
|
||||
|
||||
// for some reason, with this class it looks cleanest
|
||||
body.addClass('sidemenu-open');
|
||||
}
|
||||
setViewModeBodyClass(body, data.params.kiosk, sidemenuOpen);
|
||||
|
||||
// close all drops
|
||||
for (const drop of Drop.drops) {
|
||||
@@ -148,15 +167,37 @@ export function grafanaAppDirective(playlistSrv, contextSrv, $timeout, $rootScop
|
||||
});
|
||||
|
||||
// handle kiosk mode
|
||||
appEvents.on('toggle-kiosk-mode', () => {
|
||||
body.toggleClass('page-kiosk-mode');
|
||||
appEvents.on('toggle-kiosk-mode', options => {
|
||||
const search = $location.search();
|
||||
|
||||
if (options && options.exit) {
|
||||
search.kiosk = 1;
|
||||
}
|
||||
|
||||
switch (search.kiosk) {
|
||||
case 'tv': {
|
||||
search.kiosk = 1;
|
||||
appEvents.emit('alert-success', ['Press ESC to exit Kiosk mode']);
|
||||
break;
|
||||
}
|
||||
case 1:
|
||||
case true: {
|
||||
delete search.kiosk;
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
search.kiosk = 'tv';
|
||||
}
|
||||
}
|
||||
|
||||
$location.search(search);
|
||||
setViewModeBodyClass(body, search.kiosk, sidemenuOpen);
|
||||
});
|
||||
|
||||
// handle in active view state class
|
||||
var lastActivity = new Date().getTime();
|
||||
var activeUser = true;
|
||||
var inActiveTimeLimit = 60 * 1000;
|
||||
var sidemenuHidden = false;
|
||||
let lastActivity = new Date().getTime();
|
||||
let activeUser = true;
|
||||
const inActiveTimeLimit = 60 * 5000;
|
||||
|
||||
function checkForInActiveUser() {
|
||||
if (!activeUser) {
|
||||
@@ -169,15 +210,8 @@ export function grafanaAppDirective(playlistSrv, contextSrv, $timeout, $rootScop
|
||||
|
||||
if (new Date().getTime() - lastActivity > inActiveTimeLimit) {
|
||||
activeUser = false;
|
||||
body.addClass('user-activity-low');
|
||||
// hide sidemenu
|
||||
if (sidemenuOpen) {
|
||||
sidemenuHidden = true;
|
||||
body.removeClass('sidemenu-open');
|
||||
$timeout(function() {
|
||||
$rootScope.$broadcast('render');
|
||||
}, 100);
|
||||
}
|
||||
body.addClass('view-mode--inactive');
|
||||
body.removeClass('sidemenu-open');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -185,17 +219,8 @@ export function grafanaAppDirective(playlistSrv, contextSrv, $timeout, $rootScop
|
||||
lastActivity = new Date().getTime();
|
||||
if (!activeUser) {
|
||||
activeUser = true;
|
||||
body.removeClass('user-activity-low');
|
||||
|
||||
// restore sidemenu
|
||||
if (sidemenuHidden) {
|
||||
sidemenuHidden = false;
|
||||
body.addClass('sidemenu-open');
|
||||
appEvents.emit('toggle-inactive-mode');
|
||||
$timeout(function() {
|
||||
$rootScope.$broadcast('render');
|
||||
}, 100);
|
||||
}
|
||||
body.removeClass('view-mode--inactive');
|
||||
body.toggleClass('sidemenu-open', sidemenuOpen);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -216,19 +241,19 @@ export function grafanaAppDirective(playlistSrv, contextSrv, $timeout, $rootScop
|
||||
});
|
||||
|
||||
// handle document clicks that should hide things
|
||||
body.click(function(evt) {
|
||||
var target = $(evt.target);
|
||||
body.click(evt => {
|
||||
const target = $(evt.target);
|
||||
if (target.parents().length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// for stuff that animates, slides out etc, clicking it needs to
|
||||
// hide it right away
|
||||
var clickAutoHide = target.closest('[data-click-hide]');
|
||||
const clickAutoHide = target.closest('[data-click-hide]');
|
||||
if (clickAutoHide.length) {
|
||||
var clickAutoHideParent = clickAutoHide.parent();
|
||||
const clickAutoHideParent = clickAutoHide.parent();
|
||||
clickAutoHide.detach();
|
||||
setTimeout(function() {
|
||||
setTimeout(() => {
|
||||
clickAutoHideParent.append(clickAutoHide);
|
||||
}, 100);
|
||||
}
|
||||
@@ -240,14 +265,14 @@ export function grafanaAppDirective(playlistSrv, contextSrv, $timeout, $rootScop
|
||||
// hide search
|
||||
if (body.find('.search-container').length > 0) {
|
||||
if (target.parents('.search-results-container, .search-field-wrapper').length === 0) {
|
||||
scope.$apply(function() {
|
||||
scope.$apply(() => {
|
||||
scope.appEvent('hide-dash-search');
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// hide popovers
|
||||
var popover = elem.find('.popover');
|
||||
const popover = elem.find('.popover');
|
||||
if (popover.length > 0 && target.parents('.graph-legend').length === 0) {
|
||||
popover.hide();
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ export function infoPopover() {
|
||||
restrict: 'E',
|
||||
template: '<i class="fa fa-info-circle"></i>',
|
||||
transclude: true,
|
||||
link: function(scope, elem, attrs, ctrl, transclude) {
|
||||
link: (scope, elem, attrs, ctrl, transclude) => {
|
||||
const offset = attrs.offset || '0 -10px';
|
||||
const position = attrs.position || 'right middle';
|
||||
let classes = 'drop-help drop-hide-out-of-bounds';
|
||||
@@ -23,7 +23,7 @@ export function infoPopover() {
|
||||
elem.addClass('gf-form-help-icon--' + attrs.mode);
|
||||
}
|
||||
|
||||
transclude(function(clone, newScope) {
|
||||
transclude((clone, newScope) => {
|
||||
const content = document.createElement('div');
|
||||
content.className = 'markdown-html';
|
||||
|
||||
@@ -54,7 +54,7 @@ export function infoPopover() {
|
||||
scope.$applyAsync(() => {
|
||||
const drop = new Drop(dropOptions);
|
||||
|
||||
const unbind = scope.$on('$destroy', function() {
|
||||
const unbind = scope.$on('$destroy', () => {
|
||||
drop.destroy();
|
||||
unbind();
|
||||
});
|
||||
|
||||
@@ -12,7 +12,7 @@ function escapeString(str: string): string {
|
||||
* Determines if a value is an object
|
||||
*/
|
||||
export function isObject(value: any): boolean {
|
||||
var type = typeof value;
|
||||
const type = typeof value;
|
||||
return !!value && type === 'object';
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ export function isObject(value: any): boolean {
|
||||
* From http://stackoverflow.com/a/332429
|
||||
*
|
||||
*/
|
||||
export function getObjectName(object: Object): string {
|
||||
export function getObjectName(object: object): string {
|
||||
if (object === undefined) {
|
||||
return '';
|
||||
}
|
||||
@@ -44,7 +44,7 @@ export function getObjectName(object: Object): string {
|
||||
/*
|
||||
* Gets type of an object. Returns "null" for null objects
|
||||
*/
|
||||
export function getType(object: Object): string {
|
||||
export function getType(object: object): string {
|
||||
if (object === null) {
|
||||
return 'null';
|
||||
}
|
||||
@@ -54,8 +54,8 @@ export function getType(object: Object): string {
|
||||
/*
|
||||
* Generates inline preview for a JavaScript object based on a value
|
||||
*/
|
||||
export function getValuePreview(object: Object, value: string): string {
|
||||
var type = getType(object);
|
||||
export function getValuePreview(object: object, value: string): string {
|
||||
const type = getType(object);
|
||||
|
||||
if (type === 'null' || type === 'undefined') {
|
||||
return type;
|
||||
@@ -79,15 +79,15 @@ export function getValuePreview(object: Object, value: string): string {
|
||||
/*
|
||||
* Generates inline preview for a JavaScript object
|
||||
*/
|
||||
export function getPreview(object: string): string {
|
||||
let value = '';
|
||||
if (isObject(object)) {
|
||||
value = getObjectName(object);
|
||||
if (Array.isArray(object)) {
|
||||
value += '[' + object.length + ']';
|
||||
let value = '';
|
||||
export function getPreview(obj: object): string {
|
||||
if (isObject(obj)) {
|
||||
value = getObjectName(obj);
|
||||
if (Array.isArray(obj)) {
|
||||
value += '[' + obj.length + ']';
|
||||
}
|
||||
} else {
|
||||
value = getValuePreview(object, object);
|
||||
value = getValuePreview(obj, obj.toString());
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
@@ -14,10 +14,10 @@ const MAX_ANIMATED_TOGGLE_ITEMS = 10;
|
||||
|
||||
const requestAnimationFrame =
|
||||
window.requestAnimationFrame ||
|
||||
function(cb: () => void) {
|
||||
((cb: () => void) => {
|
||||
cb();
|
||||
return 0;
|
||||
};
|
||||
});
|
||||
|
||||
export interface JsonExplorerConfig {
|
||||
animateOpen?: boolean;
|
||||
@@ -279,7 +279,7 @@ export class JsonExplorer {
|
||||
const objectWrapperSpan = createElement('span');
|
||||
|
||||
// get constructor name and append it to wrapper span
|
||||
var constructorName = createElement('span', 'constructor-name', this.constructorName);
|
||||
const constructorName = createElement('span', 'constructor-name', this.constructorName);
|
||||
objectWrapperSpan.appendChild(constructorName);
|
||||
|
||||
// if it's an array append the array specific elements like brackets and length
|
||||
|
||||
@@ -10,8 +10,8 @@ coreModule.directive('jsonTree', [
|
||||
startExpanded: '@',
|
||||
rootName: '@',
|
||||
},
|
||||
link: function(scope, elem) {
|
||||
var jsonExp = new JsonExplorer(scope.object, 3, {
|
||||
link: (scope, elem) => {
|
||||
const jsonExp = new JsonExplorer(scope.object, 3, {
|
||||
animateOpen: true,
|
||||
});
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import store from 'app/core/store';
|
||||
import coreModule from 'app/core/core_module';
|
||||
|
||||
var template = `
|
||||
const template = `
|
||||
<div class="layout-selector">
|
||||
<button ng-click="ctrl.listView()" ng-class="{active: ctrl.mode === 'list'}">
|
||||
<i class="fa fa-list"></i>
|
||||
@@ -15,7 +15,7 @@ var template = `
|
||||
export class LayoutSelectorCtrl {
|
||||
mode: string;
|
||||
|
||||
/** @ngInject **/
|
||||
/** @ngInject */
|
||||
constructor(private $rootScope) {
|
||||
this.mode = store.get('grafana.list.layout.mode') || 'grid';
|
||||
}
|
||||
@@ -33,7 +33,7 @@ export class LayoutSelectorCtrl {
|
||||
}
|
||||
}
|
||||
|
||||
/** @ngInject **/
|
||||
/** @ngInject */
|
||||
export function layoutSelector() {
|
||||
return {
|
||||
restrict: 'E',
|
||||
@@ -45,14 +45,14 @@ export function layoutSelector() {
|
||||
};
|
||||
}
|
||||
|
||||
/** @ngInject **/
|
||||
/** @ngInject */
|
||||
export function layoutMode($rootScope) {
|
||||
return {
|
||||
restrict: 'A',
|
||||
scope: {},
|
||||
link: function(scope, elem) {
|
||||
var layout = store.get('grafana.list.layout.mode') || 'grid';
|
||||
var className = 'card-list-layout-' + layout;
|
||||
link: (scope, elem) => {
|
||||
const layout = store.get('grafana.list.layout.mode') || 'grid';
|
||||
let className = 'card-list-layout-' + layout;
|
||||
elem.addClass(className);
|
||||
|
||||
$rootScope.onAppEvent(
|
||||
|
||||
@@ -14,7 +14,7 @@ class Query {
|
||||
}
|
||||
|
||||
export class ManageDashboardsCtrl {
|
||||
public sections: any[];
|
||||
sections: any[];
|
||||
|
||||
query: Query;
|
||||
navModel: any;
|
||||
|
||||
@@ -30,7 +30,7 @@ export function navbarDirective() {
|
||||
scope: {
|
||||
model: '=',
|
||||
},
|
||||
link: function(scope, elem) {},
|
||||
link: (scope, elem) => {},
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -74,7 +74,7 @@ export class QueryPart {
|
||||
return;
|
||||
}
|
||||
|
||||
var text = this.def.type + '(';
|
||||
let text = this.def.type + '(';
|
||||
text += this.params.join(', ');
|
||||
text += ')';
|
||||
this.text = text;
|
||||
@@ -82,9 +82,9 @@ export class QueryPart {
|
||||
}
|
||||
|
||||
export function functionRenderer(part, innerExpr) {
|
||||
var str = part.def.type + '(';
|
||||
var parameters = _.map(part.params, (value, index) => {
|
||||
var paramType = part.def.params[index];
|
||||
const str = part.def.type + '(';
|
||||
const parameters = _.map(part.params, (value, index) => {
|
||||
const paramType = part.def.params[index];
|
||||
if (paramType.type === 'time') {
|
||||
if (value === 'auto') {
|
||||
value = '$__interval';
|
||||
|
||||
@@ -2,7 +2,7 @@ import _ from 'lodash';
|
||||
import $ from 'jquery';
|
||||
import coreModule from 'app/core/core_module';
|
||||
|
||||
var template = `
|
||||
const template = `
|
||||
<div class="dropdown cascade-open">
|
||||
<a ng-click="showActionsMenu()" class="query-part-name pointer dropdown-toggle" data-toggle="dropdown">{{part.def.type}}</a>
|
||||
<span>(</span><span class="query-part-parameters"></span><span>)</span>
|
||||
@@ -15,7 +15,7 @@ var template = `
|
||||
|
||||
/** @ngInject */
|
||||
export function queryPartEditorDirective($compile, templateSrv) {
|
||||
var paramTemplate = '<input type="text" class="hide input-mini tight-form-func-param"></input>';
|
||||
const paramTemplate = '<input type="text" class="hide input-mini tight-form-func-param"></input>';
|
||||
|
||||
return {
|
||||
restrict: 'E',
|
||||
@@ -26,17 +26,17 @@ export function queryPartEditorDirective($compile, templateSrv) {
|
||||
debounce: '@',
|
||||
},
|
||||
link: function postLink($scope, elem) {
|
||||
var part = $scope.part;
|
||||
var partDef = part.def;
|
||||
var $paramsContainer = elem.find('.query-part-parameters');
|
||||
var debounceLookup = $scope.debounce;
|
||||
const part = $scope.part;
|
||||
const partDef = part.def;
|
||||
const $paramsContainer = elem.find('.query-part-parameters');
|
||||
const debounceLookup = $scope.debounce;
|
||||
|
||||
$scope.partActions = [];
|
||||
|
||||
function clickFuncParam(paramIndex) {
|
||||
function clickFuncParam(this: any, paramIndex) {
|
||||
/*jshint validthis:true */
|
||||
var $link = $(this);
|
||||
var $input = $link.next();
|
||||
const $link = $(this);
|
||||
const $input = $link.next();
|
||||
|
||||
$input.val(part.params[paramIndex]);
|
||||
$input.css('width', $link.width() + 16 + 'px');
|
||||
@@ -46,18 +46,18 @@ export function queryPartEditorDirective($compile, templateSrv) {
|
||||
$input.focus();
|
||||
$input.select();
|
||||
|
||||
var typeahead = $input.data('typeahead');
|
||||
const typeahead = $input.data('typeahead');
|
||||
if (typeahead) {
|
||||
$input.val('');
|
||||
typeahead.lookup();
|
||||
}
|
||||
}
|
||||
|
||||
function inputBlur(paramIndex) {
|
||||
function inputBlur(this: any, paramIndex) {
|
||||
/*jshint validthis:true */
|
||||
var $input = $(this);
|
||||
var $link = $input.prev();
|
||||
var newValue = $input.val();
|
||||
const $input = $(this);
|
||||
const $link = $input.prev();
|
||||
const newValue = $input.val();
|
||||
|
||||
if (newValue !== '' || part.def.params[paramIndex].optional) {
|
||||
$link.html(templateSrv.highlightVariablesAsHtml(newValue));
|
||||
@@ -72,14 +72,14 @@ export function queryPartEditorDirective($compile, templateSrv) {
|
||||
$link.show();
|
||||
}
|
||||
|
||||
function inputKeyPress(paramIndex, e) {
|
||||
function inputKeyPress(this: any, paramIndex, e) {
|
||||
/*jshint validthis:true */
|
||||
if (e.which === 13) {
|
||||
inputBlur.call(this, paramIndex);
|
||||
}
|
||||
}
|
||||
|
||||
function inputKeyDown() {
|
||||
function inputKeyDown(this: any) {
|
||||
/*jshint validthis:true */
|
||||
this.style.width = (3 + this.value.length) * 8 + 'px';
|
||||
}
|
||||
@@ -89,20 +89,20 @@ export function queryPartEditorDirective($compile, templateSrv) {
|
||||
return;
|
||||
}
|
||||
|
||||
var typeaheadSource = function(query, callback) {
|
||||
const typeaheadSource = (query, callback) => {
|
||||
if (param.options) {
|
||||
var options = param.options;
|
||||
let options = param.options;
|
||||
if (param.type === 'int') {
|
||||
options = _.map(options, function(val) {
|
||||
options = _.map(options, val => {
|
||||
return val.toString();
|
||||
});
|
||||
}
|
||||
return options;
|
||||
}
|
||||
|
||||
$scope.$apply(function() {
|
||||
$scope.handleEvent({ $event: { name: 'get-param-options' } }).then(function(result) {
|
||||
var dynamicOptions = _.map(result, function(op) {
|
||||
$scope.$apply(() => {
|
||||
$scope.handleEvent({ $event: { name: 'get-param-options' } }).then(result => {
|
||||
const dynamicOptions = _.map(result, op => {
|
||||
return op.value;
|
||||
});
|
||||
callback(dynamicOptions);
|
||||
@@ -116,18 +116,18 @@ export function queryPartEditorDirective($compile, templateSrv) {
|
||||
source: typeaheadSource,
|
||||
minLength: 0,
|
||||
items: 1000,
|
||||
updater: function(value) {
|
||||
setTimeout(function() {
|
||||
updater: value => {
|
||||
setTimeout(() => {
|
||||
inputBlur.call($input[0], paramIndex);
|
||||
}, 0);
|
||||
return value;
|
||||
},
|
||||
});
|
||||
|
||||
var typeahead = $input.data('typeahead');
|
||||
const typeahead = $input.data('typeahead');
|
||||
typeahead.lookup = function() {
|
||||
this.query = this.$element.val() || '';
|
||||
var items = this.source(this.query, $.proxy(this.process, this));
|
||||
const items = this.source(this.query, $.proxy(this.process, this));
|
||||
return items ? this.process(items) : items;
|
||||
};
|
||||
|
||||
@@ -136,18 +136,18 @@ export function queryPartEditorDirective($compile, templateSrv) {
|
||||
}
|
||||
}
|
||||
|
||||
$scope.showActionsMenu = function() {
|
||||
$scope.showActionsMenu = () => {
|
||||
$scope.handleEvent({ $event: { name: 'get-part-actions' } }).then(res => {
|
||||
$scope.partActions = res;
|
||||
});
|
||||
};
|
||||
|
||||
$scope.triggerPartAction = function(action) {
|
||||
$scope.triggerPartAction = action => {
|
||||
$scope.handleEvent({ $event: { name: 'action', action: action } });
|
||||
};
|
||||
|
||||
function addElementsAndCompile() {
|
||||
_.each(partDef.params, function(param, index) {
|
||||
_.each(partDef.params, (param, index) => {
|
||||
if (param.optional && part.params.length <= index) {
|
||||
return;
|
||||
}
|
||||
@@ -156,9 +156,9 @@ export function queryPartEditorDirective($compile, templateSrv) {
|
||||
$('<span>, </span>').appendTo($paramsContainer);
|
||||
}
|
||||
|
||||
var paramValue = templateSrv.highlightVariablesAsHtml(part.params[index]);
|
||||
var $paramLink = $('<a class="graphite-func-param-link pointer">' + paramValue + '</a>');
|
||||
var $input = $(paramTemplate);
|
||||
const paramValue = templateSrv.highlightVariablesAsHtml(part.params[index]);
|
||||
const $paramLink = $('<a class="graphite-func-param-link pointer">' + paramValue + '</a>');
|
||||
const $input = $(paramTemplate);
|
||||
|
||||
$paramLink.appendTo($paramsContainer);
|
||||
$input.appendTo($paramsContainer);
|
||||
|
||||
@@ -4,7 +4,7 @@ import appEvents from 'app/core/app_events';
|
||||
export function pageScrollbar() {
|
||||
return {
|
||||
restrict: 'A',
|
||||
link: function(scope, elem, attrs) {
|
||||
link: (scope, elem, attrs) => {
|
||||
let lastPos = 0;
|
||||
|
||||
appEvents.on(
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
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">
|
||||
@@ -15,7 +14,7 @@ const scrollerClass = 'baron__scroller';
|
||||
export function geminiScrollbar() {
|
||||
return {
|
||||
restrict: 'A',
|
||||
link: function(scope, elem, attrs) {
|
||||
link: (scope, elem, attrs) => {
|
||||
let scrollRoot = elem.parent();
|
||||
const scroller = elem;
|
||||
|
||||
@@ -39,43 +38,6 @@ export function geminiScrollbar() {
|
||||
|
||||
const scrollbar = baron(scrollParams);
|
||||
|
||||
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
|
||||
);
|
||||
|
||||
// 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.dispose();
|
||||
});
|
||||
|
||||
@@ -130,8 +130,8 @@ export class SearchCtrl {
|
||||
}
|
||||
|
||||
const max = flattenedResult.length;
|
||||
let newIndex = this.selectedIndex + direction;
|
||||
this.selectedIndex = (newIndex %= max) < 0 ? newIndex + max : newIndex;
|
||||
const newIndex = (this.selectedIndex + direction) % max;
|
||||
this.selectedIndex = newIndex < 0 ? newIndex + max : newIndex;
|
||||
const selectedItem = flattenedResult[this.selectedIndex];
|
||||
|
||||
if (selectedItem.dashboardIndex === undefined && this.results[selectedItem.folderIndex].id === 0) {
|
||||
@@ -159,7 +159,7 @@ export class SearchCtrl {
|
||||
|
||||
searchDashboards() {
|
||||
this.currentSearchId = this.currentSearchId + 1;
|
||||
var localSearchId = this.currentSearchId;
|
||||
const localSearchId = this.currentSearchId;
|
||||
|
||||
return this.searchSrv.search(this.query).then(results => {
|
||||
if (localSearchId < this.currentSearchId) {
|
||||
@@ -172,7 +172,7 @@ export class SearchCtrl {
|
||||
}
|
||||
|
||||
queryHasNoFilters() {
|
||||
var query = this.query;
|
||||
const query = this.query;
|
||||
return query.query === '' && query.starred === false && query.tag.length === 0;
|
||||
}
|
||||
|
||||
|
||||
96
public/app/core/components/sidemenu/BottomNavLinks.test.tsx
Normal file
96
public/app/core/components/sidemenu/BottomNavLinks.test.tsx
Normal file
@@ -0,0 +1,96 @@
|
||||
import React from 'react';
|
||||
import { shallow } from 'enzyme';
|
||||
import BottomNavLinks from './BottomNavLinks';
|
||||
import appEvents from '../../app_events';
|
||||
|
||||
jest.mock('../../app_events', () => ({
|
||||
emit: jest.fn(),
|
||||
}));
|
||||
|
||||
const setup = (propOverrides?: object) => {
|
||||
const props = Object.assign(
|
||||
{
|
||||
link: {},
|
||||
user: {
|
||||
isGrafanaAdmin: false,
|
||||
isSignedIn: false,
|
||||
orgCount: 2,
|
||||
orgRole: '',
|
||||
orgId: 1,
|
||||
orgName: 'Grafana',
|
||||
timezone: 'UTC',
|
||||
helpFlags1: 1,
|
||||
lightTheme: false,
|
||||
hasEditPermissionInFolders: false,
|
||||
},
|
||||
},
|
||||
propOverrides
|
||||
);
|
||||
return shallow(<BottomNavLinks {...props} />);
|
||||
};
|
||||
|
||||
describe('Render', () => {
|
||||
it('should render component', () => {
|
||||
const wrapper = setup();
|
||||
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should render organisation switcher', () => {
|
||||
const wrapper = setup({
|
||||
link: {
|
||||
showOrgSwitcher: true,
|
||||
},
|
||||
});
|
||||
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should render subtitle', () => {
|
||||
const wrapper = setup({
|
||||
link: {
|
||||
subTitle: 'subtitle',
|
||||
},
|
||||
});
|
||||
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should render children', () => {
|
||||
const wrapper = setup({
|
||||
link: {
|
||||
children: [
|
||||
{
|
||||
id: '1',
|
||||
},
|
||||
{
|
||||
id: '2',
|
||||
},
|
||||
{
|
||||
id: '3',
|
||||
},
|
||||
{
|
||||
id: '4',
|
||||
hideFromMenu: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
||||
describe('Functions', () => {
|
||||
describe('item clicked', () => {
|
||||
const wrapper = setup();
|
||||
const mockEvent = { preventDefault: jest.fn() };
|
||||
it('should emit show modal event if url matches shortcut', () => {
|
||||
const child = { url: '/shortcuts' };
|
||||
const instance = wrapper.instance() as BottomNavLinks;
|
||||
instance.itemClicked(mockEvent, child);
|
||||
|
||||
expect(appEvents.emit).toHaveBeenCalledWith('show-modal', { templateHtml: '<help-modal></help-modal>' });
|
||||
});
|
||||
});
|
||||
});
|
||||
78
public/app/core/components/sidemenu/BottomNavLinks.tsx
Normal file
78
public/app/core/components/sidemenu/BottomNavLinks.tsx
Normal file
@@ -0,0 +1,78 @@
|
||||
import React, { PureComponent } from 'react';
|
||||
import appEvents from '../../app_events';
|
||||
import { User } from '../../services/context_srv';
|
||||
|
||||
export interface Props {
|
||||
link: any;
|
||||
user: User;
|
||||
}
|
||||
|
||||
class BottomNavLinks extends PureComponent<Props> {
|
||||
itemClicked = (event, child) => {
|
||||
if (child.url === '/shortcuts') {
|
||||
event.preventDefault();
|
||||
appEvents.emit('show-modal', {
|
||||
templateHtml: '<help-modal></help-modal>',
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
switchOrg = () => {
|
||||
appEvents.emit('show-modal', {
|
||||
templateHtml: '<org-switcher dismiss="dismiss()"></org-switcher>',
|
||||
});
|
||||
};
|
||||
|
||||
render() {
|
||||
const { link, user } = this.props;
|
||||
return (
|
||||
<div className="sidemenu-item dropdown dropup">
|
||||
<a href={link.url} className="sidemenu-link" target={link.target}>
|
||||
<span className="icon-circle sidemenu-icon">
|
||||
{link.icon && <i className={link.icon} />}
|
||||
{link.img && <img src={link.img} />}
|
||||
</span>
|
||||
</a>
|
||||
<ul className="dropdown-menu dropdown-menu--sidemenu" role="menu">
|
||||
{link.subTitle && (
|
||||
<li className="sidemenu-subtitle">
|
||||
<span className="sidemenu-item-text">{link.subTitle}</span>
|
||||
</li>
|
||||
)}
|
||||
{link.showOrgSwitcher && (
|
||||
<li className="sidemenu-org-switcher">
|
||||
<a onClick={this.switchOrg}>
|
||||
<div>
|
||||
<div className="sidemenu-org-switcher__org-name">{user.orgName}</div>
|
||||
<div className="sidemenu-org-switcher__org-current">Current Org:</div>
|
||||
</div>
|
||||
<div className="sidemenu-org-switcher__switch">
|
||||
<i className="fa fa-fw fa-random" />Switch
|
||||
</div>
|
||||
</a>
|
||||
</li>
|
||||
)}
|
||||
{link.children &&
|
||||
link.children.map((child, index) => {
|
||||
if (!child.hideFromMenu) {
|
||||
return (
|
||||
<li className={child.divider} key={`${child.text}-${index}`}>
|
||||
<a href={child.url} target={child.target} onClick={event => this.itemClicked(event, child)}>
|
||||
{child.icon && <i className={child.icon} />}
|
||||
{child.text}
|
||||
</a>
|
||||
</li>
|
||||
);
|
||||
}
|
||||
return null;
|
||||
})}
|
||||
<li className="side-menu-header">
|
||||
<span className="sidemenu-item-text">{link.text}</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default BottomNavLinks;
|
||||
44
public/app/core/components/sidemenu/BottomSection.test.tsx
Normal file
44
public/app/core/components/sidemenu/BottomSection.test.tsx
Normal file
@@ -0,0 +1,44 @@
|
||||
import React from 'react';
|
||||
import { shallow } from 'enzyme';
|
||||
import BottomSection from './BottomSection';
|
||||
|
||||
jest.mock('../../config', () => ({
|
||||
bootData: {
|
||||
navTree: [
|
||||
{
|
||||
id: 'profile',
|
||||
hideFromMenu: true,
|
||||
},
|
||||
{
|
||||
hideFromMenu: true,
|
||||
},
|
||||
{
|
||||
hideFromMenu: false,
|
||||
},
|
||||
{
|
||||
hideFromMenu: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
user: {
|
||||
orgCount: 5,
|
||||
orgName: 'Grafana',
|
||||
},
|
||||
}));
|
||||
|
||||
jest.mock('app/core/services/context_srv', () => ({
|
||||
contextSrv: {
|
||||
sidemenu: true,
|
||||
isSignedIn: false,
|
||||
isGrafanaAdmin: false,
|
||||
hasEditPermissionFolders: false,
|
||||
},
|
||||
}));
|
||||
|
||||
describe('Render', () => {
|
||||
it('should render component', () => {
|
||||
const wrapper = shallow(<BottomSection />);
|
||||
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
29
public/app/core/components/sidemenu/BottomSection.tsx
Normal file
29
public/app/core/components/sidemenu/BottomSection.tsx
Normal file
@@ -0,0 +1,29 @@
|
||||
import React from 'react';
|
||||
import _ from 'lodash';
|
||||
import SignIn from './SignIn';
|
||||
import BottomNavLinks from './BottomNavLinks';
|
||||
import { contextSrv } from 'app/core/services/context_srv';
|
||||
import config from '../../config';
|
||||
|
||||
export default function BottomSection() {
|
||||
const navTree = _.cloneDeep(config.bootData.navTree);
|
||||
const bottomNav = _.filter(navTree, item => item.hideFromMenu);
|
||||
const isSignedIn = contextSrv.isSignedIn;
|
||||
const user = contextSrv.user;
|
||||
|
||||
if (user && user.orgCount > 1) {
|
||||
const profileNode = _.find(bottomNav, { id: 'profile' });
|
||||
if (profileNode) {
|
||||
profileNode.showOrgSwitcher = true;
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="sidemenu__bottom">
|
||||
{!isSignedIn && <SignIn />}
|
||||
{bottomNav.map((link, index) => {
|
||||
return <BottomNavLinks link={link} user={user} key={`${link.url}-${index}`} />;
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
35
public/app/core/components/sidemenu/DropDownChild.test.tsx
Normal file
35
public/app/core/components/sidemenu/DropDownChild.test.tsx
Normal file
@@ -0,0 +1,35 @@
|
||||
import React from 'react';
|
||||
import { shallow } from 'enzyme';
|
||||
import DropDownChild from './DropDownChild';
|
||||
|
||||
const setup = (propOverrides?: object) => {
|
||||
const props = Object.assign(
|
||||
{
|
||||
child: {
|
||||
divider: true,
|
||||
},
|
||||
},
|
||||
propOverrides
|
||||
);
|
||||
|
||||
return shallow(<DropDownChild {...props} />);
|
||||
};
|
||||
|
||||
describe('Render', () => {
|
||||
it('should render component', () => {
|
||||
const wrapper = setup();
|
||||
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should render icon if exists', () => {
|
||||
const wrapper = setup({
|
||||
child: {
|
||||
divider: false,
|
||||
icon: 'icon-test',
|
||||
},
|
||||
});
|
||||
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
21
public/app/core/components/sidemenu/DropDownChild.tsx
Normal file
21
public/app/core/components/sidemenu/DropDownChild.tsx
Normal file
@@ -0,0 +1,21 @@
|
||||
import React, { SFC } from 'react';
|
||||
|
||||
export interface Props {
|
||||
child: any;
|
||||
}
|
||||
|
||||
const DropDownChild: SFC<Props> = props => {
|
||||
const { child } = props;
|
||||
const listItemClassName = child.divider ? 'divider' : '';
|
||||
|
||||
return (
|
||||
<li className={listItemClassName}>
|
||||
<a href={child.url}>
|
||||
{child.icon && <i className={child.icon} />}
|
||||
{child.text}
|
||||
</a>
|
||||
</li>
|
||||
);
|
||||
};
|
||||
|
||||
export default DropDownChild;
|
||||
70
public/app/core/components/sidemenu/SideMenu.test.tsx
Normal file
70
public/app/core/components/sidemenu/SideMenu.test.tsx
Normal file
@@ -0,0 +1,70 @@
|
||||
import React from 'react';
|
||||
import { shallow } from 'enzyme';
|
||||
import { SideMenu } from './SideMenu';
|
||||
import appEvents from '../../app_events';
|
||||
import { contextSrv } from 'app/core/services/context_srv';
|
||||
|
||||
jest.mock('../../app_events', () => ({
|
||||
emit: jest.fn(),
|
||||
}));
|
||||
|
||||
jest.mock('app/core/services/context_srv', () => ({
|
||||
contextSrv: {
|
||||
sidemenu: true,
|
||||
user: {},
|
||||
isSignedIn: false,
|
||||
isGrafanaAdmin: false,
|
||||
isEditor: false,
|
||||
hasEditPermissionFolders: false,
|
||||
toggleSideMenu: jest.fn(),
|
||||
},
|
||||
}));
|
||||
|
||||
const setup = (propOverrides?: object) => {
|
||||
const props = Object.assign(
|
||||
{
|
||||
loginUrl: '',
|
||||
user: {},
|
||||
mainLinks: [],
|
||||
bottomeLinks: [],
|
||||
isSignedIn: false,
|
||||
},
|
||||
propOverrides
|
||||
);
|
||||
|
||||
return shallow(<SideMenu {...props} />);
|
||||
};
|
||||
|
||||
describe('Render', () => {
|
||||
it('should render component', () => {
|
||||
const wrapper = setup();
|
||||
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
||||
describe('Functions', () => {
|
||||
describe('toggle side menu', () => {
|
||||
const wrapper = setup();
|
||||
const instance = wrapper.instance() as SideMenu;
|
||||
instance.toggleSideMenu();
|
||||
|
||||
it('should call contextSrv.toggleSideMenu', () => {
|
||||
expect(contextSrv.toggleSideMenu).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should emit toggle sidemenu event', () => {
|
||||
expect(appEvents.emit).toHaveBeenCalledWith('toggle-sidemenu');
|
||||
});
|
||||
});
|
||||
|
||||
describe('toggle side menu on mobile', () => {
|
||||
const wrapper = setup();
|
||||
const instance = wrapper.instance() as SideMenu;
|
||||
instance.toggleSideMenuSmallBreakpoint();
|
||||
|
||||
it('should emit toggle sidemenu event', () => {
|
||||
expect(appEvents.emit).toHaveBeenCalledWith('toggle-sidemenu-mobile');
|
||||
});
|
||||
});
|
||||
});
|
||||
32
public/app/core/components/sidemenu/SideMenu.tsx
Normal file
32
public/app/core/components/sidemenu/SideMenu.tsx
Normal file
@@ -0,0 +1,32 @@
|
||||
import React, { PureComponent } from 'react';
|
||||
import appEvents from '../../app_events';
|
||||
import { contextSrv } from 'app/core/services/context_srv';
|
||||
import TopSection from './TopSection';
|
||||
import BottomSection from './BottomSection';
|
||||
|
||||
export class SideMenu extends PureComponent {
|
||||
toggleSideMenu = () => {
|
||||
contextSrv.toggleSideMenu();
|
||||
appEvents.emit('toggle-sidemenu');
|
||||
};
|
||||
|
||||
toggleSideMenuSmallBreakpoint = () => {
|
||||
appEvents.emit('toggle-sidemenu-mobile');
|
||||
};
|
||||
|
||||
render() {
|
||||
return [
|
||||
<div className="sidemenu__logo" onClick={this.toggleSideMenu} key="logo">
|
||||
<img src="public/img/grafana_icon.svg" alt="graphana_logo" />
|
||||
</div>,
|
||||
<div className="sidemenu__logo_small_breakpoint" onClick={this.toggleSideMenuSmallBreakpoint} key="hamburger">
|
||||
<i className="fa fa-bars" />
|
||||
<span className="sidemenu__close">
|
||||
<i className="fa fa-times" /> Close
|
||||
</span>
|
||||
</div>,
|
||||
<TopSection key="topsection" />,
|
||||
<BottomSection key="bottomsection" />,
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
import React from 'react';
|
||||
import { shallow } from 'enzyme';
|
||||
import SideMenuDropDown from './SideMenuDropDown';
|
||||
|
||||
const setup = (propOverrides?: object) => {
|
||||
const props = Object.assign(
|
||||
{
|
||||
link: {
|
||||
text: 'link',
|
||||
},
|
||||
},
|
||||
propOverrides
|
||||
);
|
||||
|
||||
return shallow(<SideMenuDropDown {...props} />);
|
||||
};
|
||||
|
||||
describe('Render', () => {
|
||||
it('should render component', () => {
|
||||
const wrapper = setup();
|
||||
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should render children', () => {
|
||||
const wrapper = setup({
|
||||
link: {
|
||||
text: 'link',
|
||||
children: [{ id: 1 }, { id: 2 }, { id: 3 }],
|
||||
},
|
||||
});
|
||||
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
23
public/app/core/components/sidemenu/SideMenuDropDown.tsx
Normal file
23
public/app/core/components/sidemenu/SideMenuDropDown.tsx
Normal file
@@ -0,0 +1,23 @@
|
||||
import React, { SFC } from 'react';
|
||||
import DropDownChild from './DropDownChild';
|
||||
|
||||
interface Props {
|
||||
link: any;
|
||||
}
|
||||
|
||||
const SideMenuDropDown: SFC<Props> = props => {
|
||||
const { link } = props;
|
||||
return (
|
||||
<ul className="dropdown-menu dropdown-menu--sidemenu" role="menu">
|
||||
<li className="side-menu-header">
|
||||
<span className="sidemenu-item-text">{link.text}</span>
|
||||
</li>
|
||||
{link.children &&
|
||||
link.children.map((child, index) => {
|
||||
return <DropDownChild child={child} key={`${child.url}-${index}`} />;
|
||||
})}
|
||||
</ul>
|
||||
);
|
||||
};
|
||||
|
||||
export default SideMenuDropDown;
|
||||
11
public/app/core/components/sidemenu/SignIn.test.tsx
Normal file
11
public/app/core/components/sidemenu/SignIn.test.tsx
Normal file
@@ -0,0 +1,11 @@
|
||||
import React from 'react';
|
||||
import { shallow } from 'enzyme';
|
||||
import SignIn from './SignIn';
|
||||
|
||||
describe('Render', () => {
|
||||
it('should render component', () => {
|
||||
const wrapper = shallow(<SignIn />);
|
||||
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
23
public/app/core/components/sidemenu/SignIn.tsx
Normal file
23
public/app/core/components/sidemenu/SignIn.tsx
Normal file
@@ -0,0 +1,23 @@
|
||||
import React, { SFC } from 'react';
|
||||
|
||||
const SignIn: SFC<any> = () => {
|
||||
const loginUrl = `login?redirect=${encodeURIComponent(window.location.pathname)}`;
|
||||
return (
|
||||
<div className="sidemenu-item">
|
||||
<a href={loginUrl} className="sidemenu-link" target="_self">
|
||||
<span className="icon-circle sidemenu-icon">
|
||||
<i className="fa fa-fw fa-sign-in" />
|
||||
</span>
|
||||
</a>
|
||||
<a href={loginUrl} target="_self">
|
||||
<ul className="dropdown-menu dropdown-menu--sidemenu" role="menu">
|
||||
<li className="side-menu-header">
|
||||
<span className="sidemenu-item-text">Sign In</span>
|
||||
</li>
|
||||
</ul>
|
||||
</a>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default SignIn;
|
||||
41
public/app/core/components/sidemenu/TopSection.test.tsx
Normal file
41
public/app/core/components/sidemenu/TopSection.test.tsx
Normal file
@@ -0,0 +1,41 @@
|
||||
import React from 'react';
|
||||
import { shallow } from 'enzyme';
|
||||
import TopSection from './TopSection';
|
||||
|
||||
jest.mock('../../config', () => ({
|
||||
bootData: {
|
||||
navTree: [
|
||||
{ id: '1', hideFromMenu: true },
|
||||
{ id: '2', hideFromMenu: true },
|
||||
{ id: '3', hideFromMenu: false },
|
||||
{ id: '4', hideFromMenu: true },
|
||||
],
|
||||
},
|
||||
}));
|
||||
|
||||
const setup = (propOverrides?: object) => {
|
||||
const props = Object.assign(
|
||||
{
|
||||
mainLinks: [],
|
||||
},
|
||||
propOverrides
|
||||
);
|
||||
|
||||
return shallow(<TopSection {...props} />);
|
||||
};
|
||||
|
||||
describe('Render', () => {
|
||||
it('should render component', () => {
|
||||
const wrapper = setup();
|
||||
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should render items', () => {
|
||||
const wrapper = setup({
|
||||
mainLinks: [{ id: 1 }, { id: 2 }, { id: 3 }, { id: 4 }, { id: 5 }],
|
||||
});
|
||||
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
19
public/app/core/components/sidemenu/TopSection.tsx
Normal file
19
public/app/core/components/sidemenu/TopSection.tsx
Normal file
@@ -0,0 +1,19 @@
|
||||
import React, { SFC } from 'react';
|
||||
import _ from 'lodash';
|
||||
import TopSectionItem from './TopSectionItem';
|
||||
import config from '../../config';
|
||||
|
||||
const TopSection: SFC<any> = () => {
|
||||
const navTree = _.cloneDeep(config.bootData.navTree);
|
||||
const mainLinks = _.filter(navTree, item => !item.hideFromMenu);
|
||||
|
||||
return (
|
||||
<div className="sidemenu__top">
|
||||
{mainLinks.map((link, index) => {
|
||||
return <TopSectionItem link={link} key={`${link.id}-${index}`} />;
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default TopSection;
|
||||
22
public/app/core/components/sidemenu/TopSectionItem.test.tsx
Normal file
22
public/app/core/components/sidemenu/TopSectionItem.test.tsx
Normal file
@@ -0,0 +1,22 @@
|
||||
import React from 'react';
|
||||
import { shallow } from 'enzyme';
|
||||
import TopSectionItem from './TopSectionItem';
|
||||
|
||||
const setup = (propOverrides?: object) => {
|
||||
const props = Object.assign(
|
||||
{
|
||||
link: {},
|
||||
},
|
||||
propOverrides
|
||||
);
|
||||
|
||||
return shallow(<TopSectionItem {...props} />);
|
||||
};
|
||||
|
||||
describe('Render', () => {
|
||||
it('should render component', () => {
|
||||
const wrapper = setup();
|
||||
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
23
public/app/core/components/sidemenu/TopSectionItem.tsx
Normal file
23
public/app/core/components/sidemenu/TopSectionItem.tsx
Normal file
@@ -0,0 +1,23 @@
|
||||
import React, { SFC } from 'react';
|
||||
import SideMenuDropDown from './SideMenuDropDown';
|
||||
|
||||
export interface Props {
|
||||
link: any;
|
||||
}
|
||||
|
||||
const TopSectionItem: SFC<Props> = props => {
|
||||
const { link } = props;
|
||||
return (
|
||||
<div className="sidemenu-item dropdown">
|
||||
<a className="sidemenu-link" href={link.url} target={link.target}>
|
||||
<span className="icon-circle sidemenu-icon">
|
||||
<i className={link.icon} />
|
||||
{link.img && <img src={link.img} />}
|
||||
</span>
|
||||
</a>
|
||||
{link.children && <SideMenuDropDown link={link} />}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default TopSectionItem;
|
||||
@@ -0,0 +1,163 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`Render should render children 1`] = `
|
||||
<div
|
||||
className="sidemenu-item dropdown dropup"
|
||||
>
|
||||
<a
|
||||
className="sidemenu-link"
|
||||
>
|
||||
<span
|
||||
className="icon-circle sidemenu-icon"
|
||||
/>
|
||||
</a>
|
||||
<ul
|
||||
className="dropdown-menu dropdown-menu--sidemenu"
|
||||
role="menu"
|
||||
>
|
||||
<li
|
||||
key="undefined-0"
|
||||
>
|
||||
<a
|
||||
onClick={[Function]}
|
||||
/>
|
||||
</li>
|
||||
<li
|
||||
key="undefined-1"
|
||||
>
|
||||
<a
|
||||
onClick={[Function]}
|
||||
/>
|
||||
</li>
|
||||
<li
|
||||
key="undefined-2"
|
||||
>
|
||||
<a
|
||||
onClick={[Function]}
|
||||
/>
|
||||
</li>
|
||||
<li
|
||||
className="side-menu-header"
|
||||
>
|
||||
<span
|
||||
className="sidemenu-item-text"
|
||||
/>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`Render should render component 1`] = `
|
||||
<div
|
||||
className="sidemenu-item dropdown dropup"
|
||||
>
|
||||
<a
|
||||
className="sidemenu-link"
|
||||
>
|
||||
<span
|
||||
className="icon-circle sidemenu-icon"
|
||||
/>
|
||||
</a>
|
||||
<ul
|
||||
className="dropdown-menu dropdown-menu--sidemenu"
|
||||
role="menu"
|
||||
>
|
||||
<li
|
||||
className="side-menu-header"
|
||||
>
|
||||
<span
|
||||
className="sidemenu-item-text"
|
||||
/>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`Render should render organisation switcher 1`] = `
|
||||
<div
|
||||
className="sidemenu-item dropdown dropup"
|
||||
>
|
||||
<a
|
||||
className="sidemenu-link"
|
||||
>
|
||||
<span
|
||||
className="icon-circle sidemenu-icon"
|
||||
/>
|
||||
</a>
|
||||
<ul
|
||||
className="dropdown-menu dropdown-menu--sidemenu"
|
||||
role="menu"
|
||||
>
|
||||
<li
|
||||
className="sidemenu-org-switcher"
|
||||
>
|
||||
<a
|
||||
onClick={[Function]}
|
||||
>
|
||||
<div>
|
||||
<div
|
||||
className="sidemenu-org-switcher__org-name"
|
||||
>
|
||||
Grafana
|
||||
</div>
|
||||
<div
|
||||
className="sidemenu-org-switcher__org-current"
|
||||
>
|
||||
Current Org:
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className="sidemenu-org-switcher__switch"
|
||||
>
|
||||
<i
|
||||
className="fa fa-fw fa-random"
|
||||
/>
|
||||
Switch
|
||||
</div>
|
||||
</a>
|
||||
</li>
|
||||
<li
|
||||
className="side-menu-header"
|
||||
>
|
||||
<span
|
||||
className="sidemenu-item-text"
|
||||
/>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`Render should render subtitle 1`] = `
|
||||
<div
|
||||
className="sidemenu-item dropdown dropup"
|
||||
>
|
||||
<a
|
||||
className="sidemenu-link"
|
||||
>
|
||||
<span
|
||||
className="icon-circle sidemenu-icon"
|
||||
/>
|
||||
</a>
|
||||
<ul
|
||||
className="dropdown-menu dropdown-menu--sidemenu"
|
||||
role="menu"
|
||||
>
|
||||
<li
|
||||
className="sidemenu-subtitle"
|
||||
>
|
||||
<span
|
||||
className="sidemenu-item-text"
|
||||
>
|
||||
subtitle
|
||||
</span>
|
||||
</li>
|
||||
<li
|
||||
className="side-menu-header"
|
||||
>
|
||||
<span
|
||||
className="sidemenu-item-text"
|
||||
/>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
`;
|
||||
@@ -0,0 +1,34 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`Render should render component 1`] = `
|
||||
<div
|
||||
className="sidemenu__bottom"
|
||||
>
|
||||
<SignIn />
|
||||
<BottomNavLinks
|
||||
key="undefined-0"
|
||||
link={
|
||||
Object {
|
||||
"hideFromMenu": true,
|
||||
"id": "profile",
|
||||
}
|
||||
}
|
||||
/>
|
||||
<BottomNavLinks
|
||||
key="undefined-1"
|
||||
link={
|
||||
Object {
|
||||
"hideFromMenu": true,
|
||||
}
|
||||
}
|
||||
/>
|
||||
<BottomNavLinks
|
||||
key="undefined-2"
|
||||
link={
|
||||
Object {
|
||||
"hideFromMenu": true,
|
||||
}
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
`;
|
||||
@@ -0,0 +1,21 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`Render should render component 1`] = `
|
||||
<li
|
||||
className="divider"
|
||||
>
|
||||
<a />
|
||||
</li>
|
||||
`;
|
||||
|
||||
exports[`Render should render icon if exists 1`] = `
|
||||
<li
|
||||
className=""
|
||||
>
|
||||
<a>
|
||||
<i
|
||||
className="icon-test"
|
||||
/>
|
||||
</a>
|
||||
</li>
|
||||
`;
|
||||
@@ -0,0 +1,22 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`Render should render component 1`] = `
|
||||
Array [
|
||||
<div
|
||||
className="sidemenu__logo"
|
||||
key="logo"
|
||||
onClick={[Function]}
|
||||
/>,
|
||||
<div
|
||||
className="sidemenu__logo_small_breakpoint"
|
||||
key="hamburger"
|
||||
onClick={[Function]}
|
||||
/>,
|
||||
<TopSection
|
||||
key="topsection"
|
||||
/>,
|
||||
<BottomSection
|
||||
key="bottomsection"
|
||||
/>,
|
||||
]
|
||||
`;
|
||||
@@ -0,0 +1,59 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`Render should render children 1`] = `
|
||||
<ul
|
||||
className="dropdown-menu dropdown-menu--sidemenu"
|
||||
role="menu"
|
||||
>
|
||||
<li
|
||||
className="side-menu-header"
|
||||
>
|
||||
<span
|
||||
className="sidemenu-item-text"
|
||||
>
|
||||
link
|
||||
</span>
|
||||
</li>
|
||||
<DropDownChild
|
||||
child={
|
||||
Object {
|
||||
"id": 1,
|
||||
}
|
||||
}
|
||||
key="undefined-0"
|
||||
/>
|
||||
<DropDownChild
|
||||
child={
|
||||
Object {
|
||||
"id": 2,
|
||||
}
|
||||
}
|
||||
key="undefined-1"
|
||||
/>
|
||||
<DropDownChild
|
||||
child={
|
||||
Object {
|
||||
"id": 3,
|
||||
}
|
||||
}
|
||||
key="undefined-2"
|
||||
/>
|
||||
</ul>
|
||||
`;
|
||||
|
||||
exports[`Render should render component 1`] = `
|
||||
<ul
|
||||
className="dropdown-menu dropdown-menu--sidemenu"
|
||||
role="menu"
|
||||
>
|
||||
<li
|
||||
className="side-menu-header"
|
||||
>
|
||||
<span
|
||||
className="sidemenu-item-text"
|
||||
>
|
||||
link
|
||||
</span>
|
||||
</li>
|
||||
</ul>
|
||||
`;
|
||||
@@ -0,0 +1,40 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`Render should render component 1`] = `
|
||||
<div
|
||||
className="sidemenu-item"
|
||||
>
|
||||
<a
|
||||
className="sidemenu-link"
|
||||
href="login?redirect=blank"
|
||||
target="_self"
|
||||
>
|
||||
<span
|
||||
className="icon-circle sidemenu-icon"
|
||||
>
|
||||
<i
|
||||
className="fa fa-fw fa-sign-in"
|
||||
/>
|
||||
</span>
|
||||
</a>
|
||||
<a
|
||||
href="login?redirect=blank"
|
||||
target="_self"
|
||||
>
|
||||
<ul
|
||||
className="dropdown-menu dropdown-menu--sidemenu"
|
||||
role="menu"
|
||||
>
|
||||
<li
|
||||
className="side-menu-header"
|
||||
>
|
||||
<span
|
||||
className="sidemenu-item-text"
|
||||
>
|
||||
Sign In
|
||||
</span>
|
||||
</li>
|
||||
</ul>
|
||||
</a>
|
||||
</div>
|
||||
`;
|
||||
@@ -0,0 +1,33 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`Render should render component 1`] = `
|
||||
<div
|
||||
className="sidemenu__top"
|
||||
>
|
||||
<TopSectionItem
|
||||
key="3-0"
|
||||
link={
|
||||
Object {
|
||||
"hideFromMenu": false,
|
||||
"id": "3",
|
||||
}
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`Render should render items 1`] = `
|
||||
<div
|
||||
className="sidemenu__top"
|
||||
>
|
||||
<TopSectionItem
|
||||
key="3-0"
|
||||
link={
|
||||
Object {
|
||||
"hideFromMenu": false,
|
||||
"id": "3",
|
||||
}
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
`;
|
||||
@@ -0,0 +1,17 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`Render should render component 1`] = `
|
||||
<div
|
||||
className="sidemenu-item dropdown"
|
||||
>
|
||||
<a
|
||||
className="sidemenu-link"
|
||||
>
|
||||
<span
|
||||
className="icon-circle sidemenu-icon"
|
||||
>
|
||||
<i />
|
||||
</span>
|
||||
</a>
|
||||
</div>
|
||||
`;
|
||||
@@ -1,81 +0,0 @@
|
||||
<a class="sidemenu__logo" ng-click="ctrl.toggleSideMenu()">
|
||||
<img src="public/img/grafana_icon.svg"></img>
|
||||
</a>
|
||||
|
||||
<a class="sidemenu__logo_small_breakpoint" ng-click="ctrl.toggleSideMenuSmallBreakpoint()">
|
||||
<i class="fa fa-bars"></i>
|
||||
<span class="sidemenu__close">
|
||||
<i class="fa fa-times"></i> Close</span>
|
||||
</a>
|
||||
|
||||
<div class="sidemenu__top">
|
||||
<div ng-repeat="item in ::ctrl.mainLinks" class="sidemenu-item dropdown">
|
||||
<a href="{{::item.url}}" class="sidemenu-link" target="{{::item.target}}">
|
||||
<span class="icon-circle sidemenu-icon">
|
||||
<i class="{{::item.icon}}" ng-show="::item.icon"></i>
|
||||
<img ng-src="{{::item.img}}" ng-show="::item.img">
|
||||
</span>
|
||||
</a>
|
||||
<ul class="dropdown-menu dropdown-menu--sidemenu" role="menu" ng-if="::item.children">
|
||||
<li class="side-menu-header">
|
||||
<span class="sidemenu-item-text">{{::item.text}}</span>
|
||||
</li>
|
||||
<li ng-repeat="child in ::item.children" ng-class="{divider: child.divider}">
|
||||
<a href="{{::child.url}}">
|
||||
<i class="{{::child.icon}}" ng-show="::child.icon"></i>
|
||||
{{::child.text}}
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="sidemenu__bottom">
|
||||
<div ng-show="::!ctrl.isSignedIn" class="sidemenu-item">
|
||||
<a href="{{ctrl.loginUrl}}" class="sidemenu-link" target="_self">
|
||||
<span class="icon-circle sidemenu-icon">
|
||||
<i class="fa fa-fw fa-sign-in"></i>
|
||||
</span>
|
||||
</a>
|
||||
<a href="{{ctrl.loginUrl}}" target="_self">
|
||||
<ul class="dropdown-menu dropdown-menu--sidemenu" role="menu">
|
||||
<li class="side-menu-header">
|
||||
<span class="sidemenu-item-text">Sign In</span>
|
||||
</li>
|
||||
</ul>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div ng-repeat="item in ::ctrl.bottomNav" class="sidemenu-item dropdown dropup">
|
||||
<a href="{{::item.url}}" class="sidemenu-link" target="{{::item.target}}">
|
||||
<span class="icon-circle sidemenu-icon">
|
||||
<i class="{{::item.icon}}" ng-show="::item.icon"></i>
|
||||
<img ng-src="{{::item.img}}" ng-show="::item.img">
|
||||
</span>
|
||||
</a>
|
||||
<ul class="dropdown-menu dropdown-menu--sidemenu" role="menu">
|
||||
<li ng-if="item.subTitle" class="sidemenu-subtitle">
|
||||
<span class="sidemenu-item-text">{{::item.subTitle}}</span>
|
||||
</li>
|
||||
<li ng-if="item.showOrgSwitcher" class="sidemenu-org-switcher">
|
||||
<a ng-click="ctrl.switchOrg()">
|
||||
<div>
|
||||
<div class="sidemenu-org-switcher__org-name">{{ctrl.contextSrv.user.orgName}}</div>
|
||||
<div class="sidemenu-org-switcher__org-current">Current Org:</div>
|
||||
</div>
|
||||
<div class="sidemenu-org-switcher__switch">
|
||||
<i class="fa fa-fw fa-random"></i>Switch</div>
|
||||
</a>
|
||||
</li>
|
||||
<li ng-repeat="child in ::item.children" ng-class="{divider: child.divider}" ng-hide="::child.hideFromMenu">
|
||||
<a href="{{::child.url}}" target="{{::child.target}}" ng-click="ctrl.itemClicked(child, $event)">
|
||||
<i class="{{::child.icon}}" ng-show="::child.icon"></i>
|
||||
{{::child.text}}
|
||||
</a>
|
||||
</li>
|
||||
<li class="side-menu-header">
|
||||
<span class="sidemenu-item-text">{{::item.text}}</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1,89 +0,0 @@
|
||||
import _ from 'lodash';
|
||||
import config from 'app/core/config';
|
||||
import $ from 'jquery';
|
||||
import coreModule from '../../core_module';
|
||||
import appEvents from 'app/core/app_events';
|
||||
|
||||
export class SideMenuCtrl {
|
||||
user: any;
|
||||
mainLinks: any;
|
||||
bottomNav: any;
|
||||
loginUrl: string;
|
||||
isSignedIn: boolean;
|
||||
isOpenMobile: boolean;
|
||||
|
||||
/** @ngInject */
|
||||
constructor(private $scope, private $rootScope, private $location, private contextSrv, private $timeout) {
|
||||
this.isSignedIn = contextSrv.isSignedIn;
|
||||
this.user = contextSrv.user;
|
||||
|
||||
const navTree = _.cloneDeep(config.bootData.navTree);
|
||||
this.mainLinks = _.filter(navTree, item => !item.hideFromMenu);
|
||||
this.bottomNav = _.filter(navTree, item => item.hideFromMenu);
|
||||
this.loginUrl = 'login?redirect=' + encodeURIComponent(this.$location.path());
|
||||
|
||||
if (contextSrv.user.orgCount > 1) {
|
||||
const profileNode = _.find(this.bottomNav, { id: 'profile' });
|
||||
if (profileNode) {
|
||||
profileNode.showOrgSwitcher = true;
|
||||
}
|
||||
}
|
||||
|
||||
this.$scope.$on('$routeChangeSuccess', () => {
|
||||
this.loginUrl = 'login?redirect=' + encodeURIComponent(this.$location.path());
|
||||
});
|
||||
}
|
||||
|
||||
toggleSideMenu() {
|
||||
this.contextSrv.toggleSideMenu();
|
||||
appEvents.emit('toggle-sidemenu');
|
||||
|
||||
this.$timeout(() => {
|
||||
this.$rootScope.$broadcast('render');
|
||||
});
|
||||
}
|
||||
|
||||
toggleSideMenuSmallBreakpoint() {
|
||||
appEvents.emit('toggle-sidemenu-mobile');
|
||||
}
|
||||
|
||||
switchOrg() {
|
||||
this.$rootScope.appEvent('show-modal', {
|
||||
templateHtml: '<org-switcher dismiss="dismiss()"></org-switcher>',
|
||||
});
|
||||
}
|
||||
|
||||
itemClicked(item, evt) {
|
||||
if (item.url === '/shortcuts') {
|
||||
appEvents.emit('show-modal', {
|
||||
templateHtml: '<help-modal></help-modal>',
|
||||
});
|
||||
evt.preventDefault();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function sideMenuDirective() {
|
||||
return {
|
||||
restrict: 'E',
|
||||
templateUrl: 'public/app/core/components/sidemenu/sidemenu.html',
|
||||
controller: SideMenuCtrl,
|
||||
bindToController: true,
|
||||
controllerAs: 'ctrl',
|
||||
scope: {},
|
||||
link: function(scope, elem) {
|
||||
// hack to hide dropdown menu
|
||||
elem.on('click.dropdown', '.dropdown-menu a', function(evt) {
|
||||
var menu = $(evt.target).parents('.dropdown-menu');
|
||||
var parent = menu.parent();
|
||||
menu.detach();
|
||||
|
||||
setTimeout(function() {
|
||||
parent.append(menu);
|
||||
}, 100);
|
||||
});
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
coreModule.directive('sidemenu', sideMenuDirective);
|
||||
74
public/app/core/components/sql_part/sql_part.ts
Normal file
74
public/app/core/components/sql_part/sql_part.ts
Normal file
@@ -0,0 +1,74 @@
|
||||
import _ from 'lodash';
|
||||
|
||||
export class SqlPartDef {
|
||||
type: string;
|
||||
style: string;
|
||||
label: string;
|
||||
params: any[];
|
||||
defaultParams: any[];
|
||||
wrapOpen: string;
|
||||
wrapClose: string;
|
||||
separator: string;
|
||||
|
||||
constructor(options: any) {
|
||||
this.type = options.type;
|
||||
if (options.label) {
|
||||
this.label = options.label;
|
||||
} else {
|
||||
this.label = this.type[0].toUpperCase() + this.type.substring(1) + ':';
|
||||
}
|
||||
this.style = options.style;
|
||||
if (this.style === 'function') {
|
||||
this.wrapOpen = '(';
|
||||
this.wrapClose = ')';
|
||||
this.separator = ', ';
|
||||
} else {
|
||||
this.wrapOpen = ' ';
|
||||
this.wrapClose = ' ';
|
||||
this.separator = ' ';
|
||||
}
|
||||
this.params = options.params;
|
||||
this.defaultParams = options.defaultParams;
|
||||
}
|
||||
}
|
||||
|
||||
export class SqlPart {
|
||||
part: any;
|
||||
def: SqlPartDef;
|
||||
params: any[];
|
||||
label: string;
|
||||
name: string;
|
||||
datatype: string;
|
||||
|
||||
constructor(part: any, def: any) {
|
||||
this.part = part;
|
||||
this.def = def;
|
||||
if (!this.def) {
|
||||
throw { message: 'Could not find sql part ' + part.type };
|
||||
}
|
||||
|
||||
this.datatype = part.datatype;
|
||||
|
||||
if (part.name) {
|
||||
this.name = part.name;
|
||||
this.label = def.label + ' ' + part.name;
|
||||
} else {
|
||||
this.name = '';
|
||||
this.label = def.label;
|
||||
}
|
||||
|
||||
part.params = part.params || _.clone(this.def.defaultParams);
|
||||
this.params = part.params;
|
||||
}
|
||||
|
||||
updateParam(strValue, index) {
|
||||
// handle optional parameters
|
||||
if (strValue === '' && this.def.params[index].optional) {
|
||||
this.params.splice(index, 1);
|
||||
} else {
|
||||
this.params[index] = strValue;
|
||||
}
|
||||
|
||||
this.part.params = this.params;
|
||||
}
|
||||
}
|
||||
199
public/app/core/components/sql_part/sql_part_editor.ts
Normal file
199
public/app/core/components/sql_part/sql_part_editor.ts
Normal file
@@ -0,0 +1,199 @@
|
||||
import _ from 'lodash';
|
||||
import $ from 'jquery';
|
||||
import coreModule from 'app/core/core_module';
|
||||
|
||||
const template = `
|
||||
<div class="dropdown cascade-open">
|
||||
<a ng-click="showActionsMenu()" class="query-part-name pointer dropdown-toggle" data-toggle="dropdown">{{part.label}}</a>
|
||||
<span>{{part.def.wrapOpen}}</span><span class="query-part-parameters"></span><span>{{part.def.wrapClose}}</span>
|
||||
<ul class="dropdown-menu">
|
||||
<li ng-repeat="action in partActions">
|
||||
<a ng-click="triggerPartAction(action)">{{action.text}}</a>
|
||||
</li>
|
||||
</ul>
|
||||
`;
|
||||
|
||||
/** @ngInject */
|
||||
export function sqlPartEditorDirective($compile, templateSrv) {
|
||||
const paramTemplate = '<input type="text" class="hide input-mini"></input>';
|
||||
|
||||
return {
|
||||
restrict: 'E',
|
||||
template: template,
|
||||
scope: {
|
||||
part: '=',
|
||||
handleEvent: '&',
|
||||
debounce: '@',
|
||||
},
|
||||
link: function postLink($scope, elem) {
|
||||
const part = $scope.part;
|
||||
const partDef = part.def;
|
||||
const $paramsContainer = elem.find('.query-part-parameters');
|
||||
const debounceLookup = $scope.debounce;
|
||||
let cancelBlur = null;
|
||||
|
||||
$scope.partActions = [];
|
||||
|
||||
function clickFuncParam(this: any, paramIndex) {
|
||||
/*jshint validthis:true */
|
||||
const $link = $(this);
|
||||
const $input = $link.next();
|
||||
|
||||
$input.val(part.params[paramIndex]);
|
||||
$input.css('width', $link.width() + 16 + 'px');
|
||||
|
||||
$link.hide();
|
||||
$input.show();
|
||||
$input.focus();
|
||||
$input.select();
|
||||
|
||||
const typeahead = $input.data('typeahead');
|
||||
if (typeahead) {
|
||||
$input.val('');
|
||||
typeahead.lookup();
|
||||
}
|
||||
}
|
||||
|
||||
function inputBlur($input, paramIndex) {
|
||||
cancelBlur = setTimeout(function() {
|
||||
switchToLink($input, paramIndex);
|
||||
}, 200);
|
||||
}
|
||||
|
||||
function switchToLink($input, paramIndex) {
|
||||
/*jshint validthis:true */
|
||||
const $link = $input.prev();
|
||||
const newValue = $input.val();
|
||||
|
||||
if (newValue !== '' || part.def.params[paramIndex].optional) {
|
||||
$link.html(templateSrv.highlightVariablesAsHtml(newValue));
|
||||
|
||||
part.updateParam($input.val(), paramIndex);
|
||||
$scope.$apply(() => {
|
||||
$scope.handleEvent({ $event: { name: 'part-param-changed' } });
|
||||
});
|
||||
}
|
||||
|
||||
$input.hide();
|
||||
$link.show();
|
||||
}
|
||||
|
||||
function inputKeyPress(this: any, paramIndex, e) {
|
||||
/*jshint validthis:true */
|
||||
if (e.which === 13) {
|
||||
switchToLink($(this), paramIndex);
|
||||
}
|
||||
}
|
||||
|
||||
function inputKeyDown(this: any) {
|
||||
/*jshint validthis:true */
|
||||
this.style.width = (3 + this.value.length) * 8 + 'px';
|
||||
}
|
||||
|
||||
function addTypeahead($input, param, paramIndex) {
|
||||
if (!param.options && !param.dynamicLookup) {
|
||||
return;
|
||||
}
|
||||
|
||||
const typeaheadSource = function(query, callback) {
|
||||
if (param.options) {
|
||||
let options = param.options;
|
||||
if (param.type === 'int') {
|
||||
options = _.map(options, function(val) {
|
||||
return val.toString();
|
||||
});
|
||||
}
|
||||
return options;
|
||||
}
|
||||
|
||||
$scope.$apply(function() {
|
||||
$scope.handleEvent({ $event: { name: 'get-param-options', param: param } }).then(function(result) {
|
||||
const dynamicOptions = _.map(result, function(op) {
|
||||
return op.value;
|
||||
});
|
||||
|
||||
// add current value to dropdown if it's not in dynamicOptions
|
||||
if (_.indexOf(dynamicOptions, part.params[paramIndex]) === -1) {
|
||||
dynamicOptions.unshift(part.params[paramIndex]);
|
||||
}
|
||||
|
||||
callback(dynamicOptions);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
$input.attr('data-provide', 'typeahead');
|
||||
|
||||
$input.typeahead({
|
||||
source: typeaheadSource,
|
||||
minLength: 0,
|
||||
items: 1000,
|
||||
updater: function(value) {
|
||||
if (value === part.params[paramIndex]) {
|
||||
clearTimeout(cancelBlur);
|
||||
$input.focus();
|
||||
return value;
|
||||
}
|
||||
return value;
|
||||
},
|
||||
});
|
||||
|
||||
const typeahead = $input.data('typeahead');
|
||||
typeahead.lookup = function() {
|
||||
this.query = this.$element.val() || '';
|
||||
const items = this.source(this.query, $.proxy(this.process, this));
|
||||
return items ? this.process(items) : items;
|
||||
};
|
||||
|
||||
if (debounceLookup) {
|
||||
typeahead.lookup = _.debounce(typeahead.lookup, 500, { leading: true });
|
||||
}
|
||||
}
|
||||
|
||||
$scope.showActionsMenu = function() {
|
||||
$scope.handleEvent({ $event: { name: 'get-part-actions' } }).then(res => {
|
||||
$scope.partActions = res;
|
||||
});
|
||||
};
|
||||
|
||||
$scope.triggerPartAction = function(action) {
|
||||
$scope.handleEvent({ $event: { name: 'action', action: action } });
|
||||
};
|
||||
|
||||
function addElementsAndCompile() {
|
||||
_.each(partDef.params, function(param, index) {
|
||||
if (param.optional && part.params.length <= index) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (index > 0) {
|
||||
$('<span>' + partDef.separator + '</span>').appendTo($paramsContainer);
|
||||
}
|
||||
|
||||
const paramValue = templateSrv.highlightVariablesAsHtml(part.params[index]);
|
||||
const $paramLink = $('<a class="graphite-func-param-link pointer">' + paramValue + '</a>');
|
||||
const $input = $(paramTemplate);
|
||||
|
||||
$paramLink.appendTo($paramsContainer);
|
||||
$input.appendTo($paramsContainer);
|
||||
|
||||
$input.blur(_.partial(inputBlur, $input, index));
|
||||
$input.keyup(inputKeyDown);
|
||||
$input.keypress(_.partial(inputKeyPress, index));
|
||||
$paramLink.click(_.partial(clickFuncParam, index));
|
||||
|
||||
addTypeahead($input, param, index);
|
||||
});
|
||||
}
|
||||
|
||||
function relink() {
|
||||
$paramsContainer.empty();
|
||||
addElementsAndCompile();
|
||||
}
|
||||
|
||||
relink();
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
coreModule.directive('sqlPartEditor', sqlPartEditorDirective);
|
||||
@@ -1,6 +1,6 @@
|
||||
import coreModule from 'app/core/core_module';
|
||||
|
||||
var template = `
|
||||
const template = `
|
||||
<label for="check-{{ctrl.id}}" class="gf-form-label {{ctrl.labelClass}} pointer" ng-show="ctrl.label">
|
||||
{{ctrl.label}}
|
||||
<info-popover mode="right-normal" ng-if="ctrl.tooltip" position="top center">
|
||||
|
||||
@@ -11,9 +11,9 @@ export class Settings {
|
||||
datasources: any;
|
||||
panels: any;
|
||||
appSubUrl: string;
|
||||
window_title_prefix: string;
|
||||
windowTitlePrefix: string;
|
||||
buildInfo: BuildInfo;
|
||||
new_panel_title: string;
|
||||
newPanelTitle: string;
|
||||
bootData: any;
|
||||
externalUserMngLinkUrl: string;
|
||||
externalUserMngLinkName: string;
|
||||
@@ -33,9 +33,9 @@ export class Settings {
|
||||
constructor(options) {
|
||||
const defaults = {
|
||||
datasources: {},
|
||||
window_title_prefix: 'Grafana - ',
|
||||
windowTitlePrefix: 'Grafana - ',
|
||||
panels: {},
|
||||
new_panel_title: 'Panel Title',
|
||||
newPanelTitle: 'Panel Title',
|
||||
playlist_timespan: '1m',
|
||||
unsaved_changes_warning: true,
|
||||
appSubUrl: '',
|
||||
@@ -51,7 +51,7 @@ export class Settings {
|
||||
}
|
||||
}
|
||||
|
||||
const bootData = (<any>window).grafanaBootData || { settings: {} };
|
||||
const bootData = (window as any).grafanaBootData || { settings: {} };
|
||||
const options = bootData.settings;
|
||||
options.bootData = bootData;
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@ export class InspectCtrl {
|
||||
}
|
||||
|
||||
if (model.error.config && model.error.config.params) {
|
||||
$scope.request_parameters = _.map(model.error.config.params, function(value, key) {
|
||||
$scope.request_parameters = _.map(model.error.config.params, (value, key) => {
|
||||
return { key: key, value: value };
|
||||
});
|
||||
}
|
||||
@@ -45,7 +45,7 @@ export class InspectCtrl {
|
||||
if (_.isString(model.error.config.data)) {
|
||||
$scope.request_parameters = this.getParametersFromQueryString(model.error.config.data);
|
||||
} else {
|
||||
$scope.request_parameters = _.map(model.error.config.data, function(value, key) {
|
||||
$scope.request_parameters = _.map(model.error.config.data, (value, key) => {
|
||||
return { key: key, value: angular.toJson(value, true) };
|
||||
});
|
||||
}
|
||||
@@ -60,7 +60,7 @@ export class InspectCtrl {
|
||||
if (keyValue[1].length > 0) {
|
||||
result.push({
|
||||
key: keyValue[0],
|
||||
value: (<any>window).unescape(keyValue[1]),
|
||||
value: (window as any).unescape(keyValue[1]),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,8 +16,8 @@ export class InvitedCtrl {
|
||||
},
|
||||
};
|
||||
|
||||
$scope.init = function() {
|
||||
backendSrv.get('/api/user/invite/' + $routeParams.code).then(function(invite) {
|
||||
$scope.init = () => {
|
||||
backendSrv.get('/api/user/invite/' + $routeParams.code).then(invite => {
|
||||
$scope.formModel.name = invite.name;
|
||||
$scope.formModel.email = invite.email;
|
||||
$scope.formModel.username = invite.email;
|
||||
@@ -28,12 +28,12 @@ export class InvitedCtrl {
|
||||
});
|
||||
};
|
||||
|
||||
$scope.submit = function() {
|
||||
$scope.submit = () => {
|
||||
if (!$scope.inviteForm.$valid) {
|
||||
return;
|
||||
}
|
||||
|
||||
backendSrv.post('/api/user/invite/complete', $scope.formModel).then(function() {
|
||||
backendSrv.post('/api/user/invite/complete', $scope.formModel).then(() => {
|
||||
window.location.href = config.appSubUrl + '/';
|
||||
});
|
||||
};
|
||||
|
||||
@@ -8,7 +8,7 @@ export class JsonEditorCtrl {
|
||||
$scope.canUpdate = $scope.updateHandler !== void 0 && $scope.contextSrv.isEditor;
|
||||
$scope.canCopy = $scope.enableCopy;
|
||||
|
||||
$scope.update = function() {
|
||||
$scope.update = () => {
|
||||
const newObject = angular.fromJson($scope.json);
|
||||
$scope.updateHandler(newObject, $scope.object);
|
||||
};
|
||||
|
||||
@@ -13,6 +13,7 @@ export class LoginCtrl {
|
||||
|
||||
$scope.command = {};
|
||||
$scope.result = '';
|
||||
$scope.loggingIn = false;
|
||||
|
||||
contextSrv.sidemenu = false;
|
||||
|
||||
@@ -28,7 +29,7 @@ export class LoginCtrl {
|
||||
$scope.loginMode = true;
|
||||
$scope.submitBtnText = 'Log in';
|
||||
|
||||
$scope.init = function() {
|
||||
$scope.init = () => {
|
||||
$scope.$watch('loginMode', $scope.loginModeChanged);
|
||||
|
||||
if (config.loginError) {
|
||||
@@ -36,7 +37,7 @@ export class LoginCtrl {
|
||||
}
|
||||
};
|
||||
|
||||
$scope.submit = function() {
|
||||
$scope.submit = () => {
|
||||
if ($scope.loginMode) {
|
||||
$scope.login();
|
||||
} else {
|
||||
@@ -44,7 +45,7 @@ export class LoginCtrl {
|
||||
}
|
||||
};
|
||||
|
||||
$scope.changeView = function() {
|
||||
$scope.changeView = () => {
|
||||
const loginView = document.querySelector('#login-view');
|
||||
const changePasswordView = document.querySelector('#change-password-view');
|
||||
|
||||
@@ -64,7 +65,7 @@ export class LoginCtrl {
|
||||
}, 400);
|
||||
};
|
||||
|
||||
$scope.changePassword = function() {
|
||||
$scope.changePassword = () => {
|
||||
$scope.command.oldPassword = 'admin';
|
||||
|
||||
if ($scope.command.newPassword !== $scope.command.confirmNew) {
|
||||
@@ -72,25 +73,25 @@ export class LoginCtrl {
|
||||
return;
|
||||
}
|
||||
|
||||
backendSrv.put('/api/user/password', $scope.command).then(function() {
|
||||
backendSrv.put('/api/user/password', $scope.command).then(() => {
|
||||
$scope.toGrafana();
|
||||
});
|
||||
};
|
||||
|
||||
$scope.skip = function() {
|
||||
$scope.skip = () => {
|
||||
$scope.toGrafana();
|
||||
};
|
||||
|
||||
$scope.loginModeChanged = function(newValue) {
|
||||
$scope.loginModeChanged = newValue => {
|
||||
$scope.submitBtnText = newValue ? 'Log in' : 'Sign up';
|
||||
};
|
||||
|
||||
$scope.signUp = function() {
|
||||
$scope.signUp = () => {
|
||||
if (!$scope.loginForm.$valid) {
|
||||
return;
|
||||
}
|
||||
|
||||
backendSrv.post('/api/user/signup', $scope.formModel).then(function(result) {
|
||||
backendSrv.post('/api/user/signup', $scope.formModel).then(result => {
|
||||
if (result.status === 'SignUpCreated') {
|
||||
$location.path('/signup').search({ email: $scope.formModel.email });
|
||||
} else {
|
||||
@@ -99,25 +100,32 @@ export class LoginCtrl {
|
||||
});
|
||||
};
|
||||
|
||||
$scope.login = function() {
|
||||
$scope.login = () => {
|
||||
delete $scope.loginError;
|
||||
|
||||
if (!$scope.loginForm.$valid) {
|
||||
return;
|
||||
}
|
||||
$scope.loggingIn = true;
|
||||
|
||||
backendSrv.post('/login', $scope.formModel).then(function(result) {
|
||||
$scope.result = result;
|
||||
backendSrv
|
||||
.post('/login', $scope.formModel)
|
||||
.then(result => {
|
||||
$scope.result = result;
|
||||
|
||||
if ($scope.formModel.password !== 'admin' || $scope.ldapEnabled || $scope.authProxyEnabled) {
|
||||
$scope.toGrafana();
|
||||
return;
|
||||
}
|
||||
$scope.changeView();
|
||||
});
|
||||
if ($scope.formModel.password !== 'admin' || $scope.ldapEnabled || $scope.authProxyEnabled) {
|
||||
$scope.toGrafana();
|
||||
return;
|
||||
} else {
|
||||
$scope.changeView();
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
$scope.loggingIn = false;
|
||||
});
|
||||
};
|
||||
|
||||
$scope.toGrafana = function() {
|
||||
$scope.toGrafana = () => {
|
||||
const params = $location.search();
|
||||
|
||||
if (params.redirect && params.redirect[0] === '/') {
|
||||
|
||||
@@ -22,16 +22,16 @@ export class ResetPasswordCtrl {
|
||||
},
|
||||
};
|
||||
|
||||
$scope.sendResetEmail = function() {
|
||||
$scope.sendResetEmail = () => {
|
||||
if (!$scope.sendResetForm.$valid) {
|
||||
return;
|
||||
}
|
||||
backendSrv.post('/api/user/password/send-reset-email', $scope.formModel).then(function() {
|
||||
backendSrv.post('/api/user/password/send-reset-email', $scope.formModel).then(() => {
|
||||
$scope.mode = 'email-sent';
|
||||
});
|
||||
};
|
||||
|
||||
$scope.submitReset = function() {
|
||||
$scope.submitReset = () => {
|
||||
if (!$scope.resetForm.$valid) {
|
||||
return;
|
||||
}
|
||||
@@ -41,7 +41,7 @@ export class ResetPasswordCtrl {
|
||||
return;
|
||||
}
|
||||
|
||||
backendSrv.post('/api/user/password/reset', $scope.formModel).then(function() {
|
||||
backendSrv.post('/api/user/password/reset', $scope.formModel).then(() => {
|
||||
$location.path('login');
|
||||
});
|
||||
};
|
||||
|
||||
@@ -20,7 +20,6 @@ import './services/search_srv';
|
||||
import './services/ng_react';
|
||||
|
||||
import { grafanaAppDirective } from './components/grafana_app';
|
||||
import { sideMenuDirective } from './components/sidemenu/sidemenu';
|
||||
import { searchDirective } from './components/search/search';
|
||||
import { infoPopover } from './components/info_popover';
|
||||
import { navbarDirective } from './components/navbar/navbar';
|
||||
@@ -31,6 +30,7 @@ import { layoutSelector } from './components/layout_selector/layout_selector';
|
||||
import { switchDirective } from './components/switch';
|
||||
import { dashboardSelector } from './components/dashboard_selector';
|
||||
import { queryPartEditorDirective } from './components/query_part/query_part_editor';
|
||||
import { sqlPartEditorDirective } from './components/sql_part/sql_part_editor';
|
||||
import { formDropdownDirective } from './components/form_dropdown/form_dropdown';
|
||||
import 'app/core/controllers/all';
|
||||
import 'app/core/services/all';
|
||||
@@ -61,7 +61,6 @@ export {
|
||||
arrayJoin,
|
||||
coreModule,
|
||||
grafanaAppDirective,
|
||||
sideMenuDirective,
|
||||
navbarDirective,
|
||||
searchDirective,
|
||||
liveSrv,
|
||||
@@ -72,6 +71,7 @@ export {
|
||||
appEvents,
|
||||
dashboardSelector,
|
||||
queryPartEditorDirective,
|
||||
sqlPartEditorDirective,
|
||||
colors,
|
||||
formDropdownDirective,
|
||||
assignModelProperties,
|
||||
|
||||
@@ -7,7 +7,7 @@ export function arrayJoin() {
|
||||
return {
|
||||
restrict: 'A',
|
||||
require: 'ngModel',
|
||||
link: function(scope, element, attr, ngModel) {
|
||||
link: (scope, element, attr, ngModel) => {
|
||||
function split_array(text) {
|
||||
return (text || '').split(',');
|
||||
}
|
||||
|
||||
@@ -4,19 +4,19 @@ import coreModule from '../core_module';
|
||||
/** @ngInject */
|
||||
export function dashClass() {
|
||||
return {
|
||||
link: function($scope, elem) {
|
||||
$scope.onAppEvent('panel-fullscreen-enter', function() {
|
||||
link: ($scope, elem) => {
|
||||
$scope.onAppEvent('panel-fullscreen-enter', () => {
|
||||
elem.toggleClass('panel-in-fullscreen', true);
|
||||
});
|
||||
|
||||
$scope.onAppEvent('panel-fullscreen-exit', function() {
|
||||
$scope.onAppEvent('panel-fullscreen-exit', () => {
|
||||
elem.toggleClass('panel-in-fullscreen', false);
|
||||
});
|
||||
|
||||
$scope.$watch('ctrl.dashboardViewState.state.editview', function(newValue) {
|
||||
$scope.$watch('ctrl.dashboardViewState.state.editview', newValue => {
|
||||
if (newValue) {
|
||||
elem.toggleClass('dashboard-page--settings-opening', _.isString(newValue));
|
||||
setTimeout(function() {
|
||||
setTimeout(() => {
|
||||
elem.toggleClass('dashboard-page--settings-open', _.isString(newValue));
|
||||
}, 10);
|
||||
} else {
|
||||
|
||||
@@ -20,7 +20,7 @@ export function dropdownTypeahead($compile) {
|
||||
dropdownTypeaheadOnSelect: '&dropdownTypeaheadOnSelect',
|
||||
model: '=ngModel',
|
||||
},
|
||||
link: function($scope, elem, attrs) {
|
||||
link: ($scope, elem, attrs) => {
|
||||
const $input = $(inputTemplate);
|
||||
const $button = $(buttonTemplate);
|
||||
$input.appendTo(elem);
|
||||
@@ -31,9 +31,9 @@ export function dropdownTypeahead($compile) {
|
||||
}
|
||||
|
||||
if (attrs.ngModel) {
|
||||
$scope.$watch('model', function(newValue) {
|
||||
_.each($scope.menuItems, function(item) {
|
||||
_.each(item.submenu, function(subItem) {
|
||||
$scope.$watch('model', newValue => {
|
||||
_.each($scope.menuItems, item => {
|
||||
_.each(item.submenu, subItem => {
|
||||
if (subItem.value === newValue) {
|
||||
$button.html(subItem.text);
|
||||
}
|
||||
@@ -44,12 +44,12 @@ export function dropdownTypeahead($compile) {
|
||||
|
||||
const typeaheadValues = _.reduce(
|
||||
$scope.menuItems,
|
||||
function(memo, value, index) {
|
||||
(memo, value, index) => {
|
||||
if (!value.submenu) {
|
||||
value.click = 'menuItemSelected(' + index + ')';
|
||||
memo.push(value.text);
|
||||
} else {
|
||||
_.each(value.submenu, function(item, subIndex) {
|
||||
_.each(value.submenu, (item, subIndex) => {
|
||||
item.click = 'menuItemSelected(' + index + ',' + subIndex + ')';
|
||||
memo.push(value.text + ' ' + item.text);
|
||||
});
|
||||
@@ -59,7 +59,7 @@ export function dropdownTypeahead($compile) {
|
||||
[]
|
||||
);
|
||||
|
||||
$scope.menuItemSelected = function(index, subIndex) {
|
||||
$scope.menuItemSelected = (index, subIndex) => {
|
||||
const menuItem = $scope.menuItems[index];
|
||||
const payload: any = { $item: menuItem };
|
||||
if (menuItem.submenu && subIndex !== void 0) {
|
||||
@@ -73,10 +73,10 @@ export function dropdownTypeahead($compile) {
|
||||
source: typeaheadValues,
|
||||
minLength: 1,
|
||||
items: 10,
|
||||
updater: function(value) {
|
||||
updater: value => {
|
||||
const result: any = {};
|
||||
_.each($scope.menuItems, function(menuItem) {
|
||||
_.each(menuItem.submenu, function(submenuItem) {
|
||||
_.each($scope.menuItems, menuItem => {
|
||||
_.each(menuItem.submenu, submenuItem => {
|
||||
if (value === menuItem.text + ' ' + submenuItem.text) {
|
||||
result.$subItem = submenuItem;
|
||||
result.$item = menuItem;
|
||||
@@ -85,7 +85,7 @@ export function dropdownTypeahead($compile) {
|
||||
});
|
||||
|
||||
if (result.$item) {
|
||||
$scope.$apply(function() {
|
||||
$scope.$apply(() => {
|
||||
$scope.dropdownTypeaheadOnSelect(result);
|
||||
});
|
||||
}
|
||||
@@ -95,24 +95,24 @@ export function dropdownTypeahead($compile) {
|
||||
},
|
||||
});
|
||||
|
||||
$button.click(function() {
|
||||
$button.click(() => {
|
||||
$button.hide();
|
||||
$input.show();
|
||||
$input.focus();
|
||||
});
|
||||
|
||||
$input.keyup(function() {
|
||||
$input.keyup(() => {
|
||||
elem.toggleClass('open', $input.val() === '');
|
||||
});
|
||||
|
||||
$input.blur(function() {
|
||||
$input.blur(() => {
|
||||
$input.hide();
|
||||
$input.val('');
|
||||
$button.show();
|
||||
$button.focus();
|
||||
// clicking the function dropdown menu won't
|
||||
// work if you remove class at once
|
||||
setTimeout(function() {
|
||||
setTimeout(() => {
|
||||
elem.removeClass('open');
|
||||
}, 200);
|
||||
});
|
||||
@@ -138,7 +138,7 @@ export function dropdownTypeahead2($compile) {
|
||||
dropdownTypeaheadOnSelect: '&dropdownTypeaheadOnSelect',
|
||||
model: '=ngModel',
|
||||
},
|
||||
link: function($scope, elem, attrs) {
|
||||
link: ($scope, elem, attrs) => {
|
||||
const $input = $(inputTemplate);
|
||||
const $button = $(buttonTemplate);
|
||||
$input.appendTo(elem);
|
||||
@@ -149,9 +149,9 @@ export function dropdownTypeahead2($compile) {
|
||||
}
|
||||
|
||||
if (attrs.ngModel) {
|
||||
$scope.$watch('model', function(newValue) {
|
||||
_.each($scope.menuItems, function(item) {
|
||||
_.each(item.submenu, function(subItem) {
|
||||
$scope.$watch('model', newValue => {
|
||||
_.each($scope.menuItems, item => {
|
||||
_.each(item.submenu, subItem => {
|
||||
if (subItem.value === newValue) {
|
||||
$button.html(subItem.text);
|
||||
}
|
||||
@@ -162,12 +162,12 @@ export function dropdownTypeahead2($compile) {
|
||||
|
||||
const typeaheadValues = _.reduce(
|
||||
$scope.menuItems,
|
||||
function(memo, value, index) {
|
||||
(memo, value, index) => {
|
||||
if (!value.submenu) {
|
||||
value.click = 'menuItemSelected(' + index + ')';
|
||||
memo.push(value.text);
|
||||
} else {
|
||||
_.each(value.submenu, function(item, subIndex) {
|
||||
_.each(value.submenu, (item, subIndex) => {
|
||||
item.click = 'menuItemSelected(' + index + ',' + subIndex + ')';
|
||||
memo.push(value.text + ' ' + item.text);
|
||||
});
|
||||
@@ -177,7 +177,7 @@ export function dropdownTypeahead2($compile) {
|
||||
[]
|
||||
);
|
||||
|
||||
$scope.menuItemSelected = function(index, subIndex) {
|
||||
$scope.menuItemSelected = (index, subIndex) => {
|
||||
const menuItem = $scope.menuItems[index];
|
||||
const payload: any = { $item: menuItem };
|
||||
if (menuItem.submenu && subIndex !== void 0) {
|
||||
@@ -191,10 +191,10 @@ export function dropdownTypeahead2($compile) {
|
||||
source: typeaheadValues,
|
||||
minLength: 1,
|
||||
items: 10,
|
||||
updater: function(value) {
|
||||
updater: value => {
|
||||
const result: any = {};
|
||||
_.each($scope.menuItems, function(menuItem) {
|
||||
_.each(menuItem.submenu, function(submenuItem) {
|
||||
_.each($scope.menuItems, menuItem => {
|
||||
_.each(menuItem.submenu, submenuItem => {
|
||||
if (value === menuItem.text + ' ' + submenuItem.text) {
|
||||
result.$subItem = submenuItem;
|
||||
result.$item = menuItem;
|
||||
@@ -203,7 +203,7 @@ export function dropdownTypeahead2($compile) {
|
||||
});
|
||||
|
||||
if (result.$item) {
|
||||
$scope.$apply(function() {
|
||||
$scope.$apply(() => {
|
||||
$scope.dropdownTypeaheadOnSelect(result);
|
||||
});
|
||||
}
|
||||
@@ -213,24 +213,24 @@ export function dropdownTypeahead2($compile) {
|
||||
},
|
||||
});
|
||||
|
||||
$button.click(function() {
|
||||
$button.click(() => {
|
||||
$button.hide();
|
||||
$input.show();
|
||||
$input.focus();
|
||||
});
|
||||
|
||||
$input.keyup(function() {
|
||||
$input.keyup(() => {
|
||||
elem.toggleClass('open', $input.val() === '');
|
||||
});
|
||||
|
||||
$input.blur(function() {
|
||||
$input.blur(() => {
|
||||
$input.hide();
|
||||
$input.val('');
|
||||
$button.show();
|
||||
$button.focus();
|
||||
// clicking the function dropdown menu won't
|
||||
// work if you remove class at once
|
||||
setTimeout(function() {
|
||||
setTimeout(() => {
|
||||
elem.removeClass('open');
|
||||
}, 200);
|
||||
});
|
||||
|
||||
@@ -1,22 +1,22 @@
|
||||
import coreModule from '../core_module';
|
||||
|
||||
coreModule.directive('giveFocus', function() {
|
||||
return function(scope, element, attrs) {
|
||||
element.click(function(e) {
|
||||
coreModule.directive('giveFocus', () => {
|
||||
return (scope, element, attrs) => {
|
||||
element.click(e => {
|
||||
e.stopPropagation();
|
||||
});
|
||||
|
||||
scope.$watch(
|
||||
attrs.giveFocus,
|
||||
function(newValue) {
|
||||
newValue => {
|
||||
if (!newValue) {
|
||||
return;
|
||||
}
|
||||
setTimeout(function() {
|
||||
setTimeout(() => {
|
||||
element.focus();
|
||||
var domEl = element[0];
|
||||
const domEl = element[0];
|
||||
if (domEl.setSelectionRange) {
|
||||
var pos = element.val().length * 2;
|
||||
const pos = element.val().length * 2;
|
||||
domEl.setSelectionRange(pos, pos);
|
||||
}
|
||||
}, 200);
|
||||
|
||||
@@ -24,7 +24,7 @@ export function metricSegment($compile, $sce) {
|
||||
onChange: '&',
|
||||
debounce: '@',
|
||||
},
|
||||
link: function($scope, elem) {
|
||||
link: ($scope, elem) => {
|
||||
const $input = $(inputTemplate);
|
||||
const segment = $scope.segment;
|
||||
const $button = $(segment.selectMode ? selectTemplate : linkTemplate);
|
||||
@@ -36,14 +36,14 @@ export function metricSegment($compile, $sce) {
|
||||
$input.appendTo(elem);
|
||||
$button.appendTo(elem);
|
||||
|
||||
$scope.updateVariableValue = function(value) {
|
||||
$scope.updateVariableValue = value => {
|
||||
if (value === '' || segment.value === value) {
|
||||
return;
|
||||
}
|
||||
|
||||
value = _.unescape(value);
|
||||
|
||||
$scope.$apply(function() {
|
||||
$scope.$apply(() => {
|
||||
const selected = _.find($scope.altSegments, { value: value });
|
||||
if (selected) {
|
||||
segment.value = selected.value;
|
||||
@@ -65,7 +65,7 @@ export function metricSegment($compile, $sce) {
|
||||
});
|
||||
};
|
||||
|
||||
$scope.switchToLink = function(fromClick) {
|
||||
$scope.switchToLink = fromClick => {
|
||||
if (linkMode && !fromClick) {
|
||||
return;
|
||||
}
|
||||
@@ -78,17 +78,17 @@ export function metricSegment($compile, $sce) {
|
||||
$scope.updateVariableValue($input.val());
|
||||
};
|
||||
|
||||
$scope.inputBlur = function() {
|
||||
$scope.inputBlur = () => {
|
||||
// happens long before the click event on the typeahead options
|
||||
// need to have long delay because the blur
|
||||
cancelBlur = setTimeout($scope.switchToLink, 200);
|
||||
};
|
||||
|
||||
$scope.source = function(query, callback) {
|
||||
$scope.$apply(function() {
|
||||
$scope.getOptions({ $query: query }).then(function(altSegments) {
|
||||
$scope.source = (query, callback) => {
|
||||
$scope.$apply(() => {
|
||||
$scope.getOptions({ $query: query }).then(altSegments => {
|
||||
$scope.altSegments = altSegments;
|
||||
options = _.map($scope.altSegments, function(alt) {
|
||||
options = _.map($scope.altSegments, alt => {
|
||||
return _.escape(alt.value);
|
||||
});
|
||||
|
||||
@@ -104,7 +104,7 @@ export function metricSegment($compile, $sce) {
|
||||
});
|
||||
};
|
||||
|
||||
$scope.updater = function(value) {
|
||||
$scope.updater = value => {
|
||||
if (value === segment.value) {
|
||||
clearTimeout(cancelBlur);
|
||||
$input.focus();
|
||||
@@ -152,14 +152,14 @@ export function metricSegment($compile, $sce) {
|
||||
typeahead.lookup = _.debounce(typeahead.lookup, 500, { leading: true });
|
||||
}
|
||||
|
||||
$button.keydown(function(evt) {
|
||||
$button.keydown(evt => {
|
||||
// trigger typeahead on down arrow or enter key
|
||||
if (evt.keyCode === 40 || evt.keyCode === 13) {
|
||||
$button.click();
|
||||
}
|
||||
});
|
||||
|
||||
$button.click(function() {
|
||||
$button.click(() => {
|
||||
options = null;
|
||||
$input.css('width', Math.max($button.width(), 80) + 16 + 'px');
|
||||
|
||||
@@ -199,7 +199,7 @@ export function metricSegmentModel(uiSegmentSrv, $q) {
|
||||
pre: function postLink($scope, elem, attrs) {
|
||||
let cachedOptions;
|
||||
|
||||
$scope.valueToSegment = function(value) {
|
||||
$scope.valueToSegment = value => {
|
||||
const option = _.find($scope.options, { value: value });
|
||||
const segment = {
|
||||
cssClass: attrs.cssClass,
|
||||
@@ -211,18 +211,18 @@ export function metricSegmentModel(uiSegmentSrv, $q) {
|
||||
return uiSegmentSrv.newSegment(segment);
|
||||
};
|
||||
|
||||
$scope.getOptionsInternal = function() {
|
||||
$scope.getOptionsInternal = () => {
|
||||
if ($scope.options) {
|
||||
cachedOptions = $scope.options;
|
||||
return $q.when(
|
||||
_.map($scope.options, function(option) {
|
||||
_.map($scope.options, option => {
|
||||
return { value: option.text };
|
||||
})
|
||||
);
|
||||
} else {
|
||||
return $scope.getOptions().then(function(options) {
|
||||
return $scope.getOptions().then(options => {
|
||||
cachedOptions = options;
|
||||
return _.map(options, function(option) {
|
||||
return _.map(options, option => {
|
||||
if (option.html) {
|
||||
return option;
|
||||
}
|
||||
@@ -232,7 +232,7 @@ export function metricSegmentModel(uiSegmentSrv, $q) {
|
||||
}
|
||||
};
|
||||
|
||||
$scope.onSegmentChange = function() {
|
||||
$scope.onSegmentChange = () => {
|
||||
if (cachedOptions) {
|
||||
const option = _.find(cachedOptions, { text: $scope.segment.value });
|
||||
if (option && option.value !== $scope.property) {
|
||||
@@ -246,8 +246,8 @@ export function metricSegmentModel(uiSegmentSrv, $q) {
|
||||
|
||||
// needs to call this after digest so
|
||||
// property is synced with outerscope
|
||||
$scope.$$postDigest(function() {
|
||||
$scope.$apply(function() {
|
||||
$scope.$$postDigest(() => {
|
||||
$scope.$apply(() => {
|
||||
$scope.onChange();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -8,8 +8,8 @@ import { appEvents } from 'app/core/core';
|
||||
function tip($compile) {
|
||||
return {
|
||||
restrict: 'E',
|
||||
link: function(scope, elem, attrs) {
|
||||
var _t =
|
||||
link: (scope, elem, attrs) => {
|
||||
let _t =
|
||||
'<i class="grafana-tip fa fa-' +
|
||||
(attrs.icon || 'question-circle') +
|
||||
'" bs-tooltip="\'' +
|
||||
@@ -26,9 +26,9 @@ function clipboardButton() {
|
||||
scope: {
|
||||
getText: '&clipboardButton',
|
||||
},
|
||||
link: function(scope, elem) {
|
||||
link: (scope, elem) => {
|
||||
scope.clipboard = new Clipboard(elem[0], {
|
||||
text: function() {
|
||||
text: () => {
|
||||
return scope.getText();
|
||||
},
|
||||
});
|
||||
@@ -37,7 +37,7 @@ function clipboardButton() {
|
||||
appEvents.emit('alert-success', ['Content copied to clipboard']);
|
||||
});
|
||||
|
||||
scope.$on('$destroy', function() {
|
||||
scope.$on('$destroy', () => {
|
||||
if (scope.clipboard) {
|
||||
scope.clipboard.destroy();
|
||||
}
|
||||
@@ -50,12 +50,12 @@ function clipboardButton() {
|
||||
function compile($compile) {
|
||||
return {
|
||||
restrict: 'A',
|
||||
link: function(scope, element, attrs) {
|
||||
link: (scope, element, attrs) => {
|
||||
scope.$watch(
|
||||
function(scope) {
|
||||
scope => {
|
||||
return scope.$eval(attrs.compile);
|
||||
},
|
||||
function(value) {
|
||||
value => {
|
||||
element.html(value);
|
||||
$compile(element.contents())(scope);
|
||||
}
|
||||
@@ -67,9 +67,9 @@ function compile($compile) {
|
||||
function watchChange() {
|
||||
return {
|
||||
scope: { onchange: '&watchChange' },
|
||||
link: function(scope, element) {
|
||||
element.on('input', function() {
|
||||
scope.$apply(function() {
|
||||
link: (scope, element) => {
|
||||
element.on('input', () => {
|
||||
scope.$apply(() => {
|
||||
scope.onchange({ inputValue: element.val() });
|
||||
});
|
||||
});
|
||||
@@ -81,12 +81,12 @@ function watchChange() {
|
||||
function editorOptBool($compile) {
|
||||
return {
|
||||
restrict: 'E',
|
||||
link: function(scope, elem, attrs) {
|
||||
var ngchange = attrs.change ? ' ng-change="' + attrs.change + '"' : '';
|
||||
var tip = attrs.tip ? ' <tip>' + attrs.tip + '</tip>' : '';
|
||||
var showIf = attrs.showIf ? ' ng-show="' + attrs.showIf + '" ' : '';
|
||||
link: (scope, elem, attrs) => {
|
||||
const ngchange = attrs.change ? ' ng-change="' + attrs.change + '"' : '';
|
||||
const tip = attrs.tip ? ' <tip>' + attrs.tip + '</tip>' : '';
|
||||
const showIf = attrs.showIf ? ' ng-show="' + attrs.showIf + '" ' : '';
|
||||
|
||||
var template =
|
||||
const template =
|
||||
'<div class="editor-option gf-form-checkbox text-center"' +
|
||||
showIf +
|
||||
'>' +
|
||||
@@ -118,14 +118,14 @@ function editorOptBool($compile) {
|
||||
function editorCheckbox($compile, $interpolate) {
|
||||
return {
|
||||
restrict: 'E',
|
||||
link: function(scope, elem, attrs) {
|
||||
var text = $interpolate(attrs.text)(scope);
|
||||
var model = $interpolate(attrs.model)(scope);
|
||||
var ngchange = attrs.change ? ' ng-change="' + attrs.change + '"' : '';
|
||||
var tip = attrs.tip ? ' <tip>' + attrs.tip + '</tip>' : '';
|
||||
var label = '<label for="' + scope.$id + model + '" class="checkbox-label">' + text + tip + '</label>';
|
||||
link: (scope, elem, attrs) => {
|
||||
const text = $interpolate(attrs.text)(scope);
|
||||
const model = $interpolate(attrs.model)(scope);
|
||||
const ngchange = attrs.change ? ' ng-change="' + attrs.change + '"' : '';
|
||||
const tip = attrs.tip ? ' <tip>' + attrs.tip + '</tip>' : '';
|
||||
const label = '<label for="' + scope.$id + model + '" class="checkbox-label">' + text + tip + '</label>';
|
||||
|
||||
var template =
|
||||
let template =
|
||||
'<input class="cr1" id="' +
|
||||
scope.$id +
|
||||
model +
|
||||
@@ -152,8 +152,8 @@ function editorCheckbox($compile, $interpolate) {
|
||||
/** @ngInject */
|
||||
function gfDropdown($parse, $compile, $timeout) {
|
||||
function buildTemplate(items, placement?) {
|
||||
var upclass = placement === 'top' ? 'dropup' : '';
|
||||
var ul = ['<ul class="dropdown-menu ' + upclass + '" role="menu" aria-labelledby="drop1">', '</ul>'];
|
||||
const upclass = placement === 'top' ? 'dropup' : '';
|
||||
const ul = ['<ul class="dropdown-menu ' + upclass + '" role="menu" aria-labelledby="drop1">', '</ul>'];
|
||||
|
||||
for (let index = 0; index < items.length; index++) {
|
||||
const item = items[index];
|
||||
@@ -163,7 +163,7 @@ function gfDropdown($parse, $compile, $timeout) {
|
||||
continue;
|
||||
}
|
||||
|
||||
var li =
|
||||
let li =
|
||||
'<li' +
|
||||
(item.submenu && item.submenu.length ? ' class="dropdown-submenu"' : '') +
|
||||
'>' +
|
||||
@@ -192,11 +192,11 @@ function gfDropdown($parse, $compile, $timeout) {
|
||||
restrict: 'EA',
|
||||
scope: true,
|
||||
link: function postLink(scope, iElement, iAttrs) {
|
||||
var getter = $parse(iAttrs.gfDropdown),
|
||||
const getter = $parse(iAttrs.gfDropdown),
|
||||
items = getter(scope);
|
||||
$timeout(function() {
|
||||
var placement = iElement.data('placement');
|
||||
var dropdown = angular.element(buildTemplate(items, placement).join(''));
|
||||
$timeout(() => {
|
||||
const placement = iElement.data('placement');
|
||||
const dropdown = angular.element(buildTemplate(items, placement).join(''));
|
||||
dropdown.insertAfter(iElement);
|
||||
$compile(iElement.next('ul.dropdown-menu'))(scope);
|
||||
});
|
||||
|
||||
@@ -6,14 +6,14 @@ function ngModelOnBlur() {
|
||||
restrict: 'A',
|
||||
priority: 1,
|
||||
require: 'ngModel',
|
||||
link: function(scope, elm, attr, ngModelCtrl) {
|
||||
link: (scope, elm, attr, ngModelCtrl) => {
|
||||
if (attr.type === 'radio' || attr.type === 'checkbox') {
|
||||
return;
|
||||
}
|
||||
|
||||
elm.off('input keydown change');
|
||||
elm.bind('blur', function() {
|
||||
scope.$apply(function() {
|
||||
elm.bind('blur', () => {
|
||||
scope.$apply(() => {
|
||||
ngModelCtrl.$setViewValue(elm.val());
|
||||
});
|
||||
});
|
||||
@@ -25,8 +25,8 @@ function emptyToNull() {
|
||||
return {
|
||||
restrict: 'A',
|
||||
require: 'ngModel',
|
||||
link: function(scope, elm, attrs, ctrl) {
|
||||
ctrl.$parsers.push(function(viewValue) {
|
||||
link: (scope, elm, attrs, ctrl) => {
|
||||
ctrl.$parsers.push(viewValue => {
|
||||
if (viewValue === '') {
|
||||
return null;
|
||||
}
|
||||
@@ -39,15 +39,15 @@ function emptyToNull() {
|
||||
function validTimeSpan() {
|
||||
return {
|
||||
require: 'ngModel',
|
||||
link: function(scope, elm, attrs, ctrl) {
|
||||
ctrl.$validators.integer = function(modelValue, viewValue) {
|
||||
link: (scope, elm, attrs, ctrl) => {
|
||||
ctrl.$validators.integer = (modelValue, viewValue) => {
|
||||
if (ctrl.$isEmpty(modelValue)) {
|
||||
return true;
|
||||
}
|
||||
if (viewValue.indexOf('$') === 0 || viewValue.indexOf('+$') === 0) {
|
||||
return true; // allow template variable
|
||||
}
|
||||
var info = rangeUtil.describeTextRange(viewValue);
|
||||
const info = rangeUtil.describeTextRange(viewValue);
|
||||
return info.invalid !== true;
|
||||
};
|
||||
},
|
||||
|
||||
@@ -2,23 +2,25 @@ import $ from 'jquery';
|
||||
import coreModule from '../core_module';
|
||||
|
||||
function getBlockNodes(nodes) {
|
||||
var node = nodes[0];
|
||||
var endNode = nodes[nodes.length - 1];
|
||||
var blockNodes;
|
||||
let node = nodes[0];
|
||||
const endNode = nodes[nodes.length - 1];
|
||||
let blockNodes;
|
||||
node = node.nextSibling;
|
||||
|
||||
for (var i = 1; node !== endNode && (node = node.nextSibling); i++) {
|
||||
for (let i = 1; node !== endNode && node; i++) {
|
||||
if (blockNodes || nodes[i] !== node) {
|
||||
if (!blockNodes) {
|
||||
blockNodes = $([].slice.call(nodes, 0, i));
|
||||
}
|
||||
blockNodes.push(node);
|
||||
}
|
||||
node = node.nextSibling;
|
||||
}
|
||||
|
||||
return blockNodes || nodes;
|
||||
}
|
||||
|
||||
/** @ngInject **/
|
||||
/** @ngInject */
|
||||
function rebuildOnChange($animate) {
|
||||
return {
|
||||
multiElement: true,
|
||||
@@ -26,8 +28,8 @@ function rebuildOnChange($animate) {
|
||||
transclude: true,
|
||||
priority: 600,
|
||||
restrict: 'E',
|
||||
link: function(scope, elem, attrs, ctrl, transclude) {
|
||||
var block, childScope, previousElements;
|
||||
link: (scope, elem, attrs, ctrl, transclude) => {
|
||||
let block, childScope, previousElements;
|
||||
|
||||
function cleanUp() {
|
||||
if (previousElements) {
|
||||
@@ -40,7 +42,7 @@ function rebuildOnChange($animate) {
|
||||
}
|
||||
if (block) {
|
||||
previousElements = getBlockNodes(block.clone);
|
||||
$animate.leave(previousElements).then(function() {
|
||||
$animate.leave(previousElements).then(() => {
|
||||
previousElements = null;
|
||||
});
|
||||
block = null;
|
||||
@@ -53,7 +55,7 @@ function rebuildOnChange($animate) {
|
||||
}
|
||||
|
||||
if (!childScope && (value || attrs.showNull)) {
|
||||
transclude(function(clone, newScope) {
|
||||
transclude((clone, newScope) => {
|
||||
childScope = newScope;
|
||||
clone[clone.length++] = document.createComment(' end rebuild on change ');
|
||||
block = { clone: clone };
|
||||
|
||||
@@ -13,7 +13,7 @@ function setColor(name, element) {
|
||||
function tagColorFromName() {
|
||||
return {
|
||||
scope: { tagColorFromName: '=' },
|
||||
link: function(scope, element) {
|
||||
link: (scope, element) => {
|
||||
setColor(scope.tagColorFromName, element);
|
||||
},
|
||||
};
|
||||
@@ -29,7 +29,7 @@ function bootstrapTagsinput() {
|
||||
return scope.$parent[property];
|
||||
}
|
||||
|
||||
return function(item) {
|
||||
return item => {
|
||||
return item[property];
|
||||
};
|
||||
}
|
||||
@@ -47,7 +47,7 @@ function bootstrapTagsinput() {
|
||||
scope.model = [];
|
||||
}
|
||||
|
||||
var select = $('select', element);
|
||||
const select = $('select', element);
|
||||
|
||||
if (attrs.placeholder) {
|
||||
select.attr('placeholder', attrs.placeholder);
|
||||
@@ -64,29 +64,29 @@ function bootstrapTagsinput() {
|
||||
itemText: getItemProperty(scope, attrs.itemtext),
|
||||
tagClass: angular.isFunction(scope.$parent[attrs.tagclass])
|
||||
? scope.$parent[attrs.tagclass]
|
||||
: function() {
|
||||
: () => {
|
||||
return attrs.tagclass;
|
||||
},
|
||||
});
|
||||
|
||||
select.on('itemAdded', function(event) {
|
||||
select.on('itemAdded', event => {
|
||||
if (scope.model.indexOf(event.item) === -1) {
|
||||
scope.model.push(event.item);
|
||||
if (scope.onTagsUpdated) {
|
||||
scope.onTagsUpdated();
|
||||
}
|
||||
}
|
||||
var tagElement = select
|
||||
const tagElement = select
|
||||
.next()
|
||||
.children('span')
|
||||
.filter(function() {
|
||||
.filter(() => {
|
||||
return $(this).text() === event.item;
|
||||
});
|
||||
setColor(event.item, tagElement);
|
||||
});
|
||||
|
||||
select.on('itemRemoved', function(event) {
|
||||
var idx = scope.model.indexOf(event.item);
|
||||
select.on('itemRemoved', event => {
|
||||
const idx = scope.model.indexOf(event.item);
|
||||
if (idx !== -1) {
|
||||
scope.model.splice(idx, 1);
|
||||
if (scope.onTagsUpdated) {
|
||||
@@ -97,14 +97,14 @@ function bootstrapTagsinput() {
|
||||
|
||||
scope.$watch(
|
||||
'model',
|
||||
function() {
|
||||
() => {
|
||||
if (!angular.isArray(scope.model)) {
|
||||
scope.model = [];
|
||||
}
|
||||
|
||||
select.tagsinput('removeAll');
|
||||
|
||||
for (var i = 0; i < scope.model.length; i++) {
|
||||
for (let i = 0; i < scope.model.length; i++) {
|
||||
select.tagsinput('add', scope.model[i]);
|
||||
}
|
||||
},
|
||||
|
||||
@@ -245,7 +245,7 @@ export function valueSelectDropdown($compile, $window, $timeout, $rootScope) {
|
||||
controller: 'ValueSelectDropdownCtrl',
|
||||
controllerAs: 'vm',
|
||||
bindToController: true,
|
||||
link: function(scope, elem) {
|
||||
link: (scope, elem) => {
|
||||
const bodyEl = angular.element($window.document.body);
|
||||
const linkEl = elem.find('.variable-value-link');
|
||||
const inputEl = elem.find('input');
|
||||
@@ -258,7 +258,7 @@ export function valueSelectDropdown($compile, $window, $timeout, $rootScope) {
|
||||
|
||||
inputEl.focus();
|
||||
$timeout(
|
||||
function() {
|
||||
() => {
|
||||
bodyEl.on('click', bodyOnClick);
|
||||
},
|
||||
0,
|
||||
@@ -274,7 +274,7 @@ export function valueSelectDropdown($compile, $window, $timeout, $rootScope) {
|
||||
|
||||
function bodyOnClick(e) {
|
||||
if (elem.has(e.target).length === 0) {
|
||||
scope.$apply(function() {
|
||||
scope.$apply(() => {
|
||||
scope.vm.commitChanges();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -3,22 +3,22 @@ import angular from 'angular';
|
||||
import moment from 'moment';
|
||||
import coreModule from '../core_module';
|
||||
|
||||
coreModule.filter('stringSort', function() {
|
||||
return function(input) {
|
||||
coreModule.filter('stringSort', () => {
|
||||
return input => {
|
||||
return input.sort();
|
||||
};
|
||||
});
|
||||
|
||||
coreModule.filter('slice', function() {
|
||||
return function(arr, start, end) {
|
||||
coreModule.filter('slice', () => {
|
||||
return (arr, start, end) => {
|
||||
if (!_.isUndefined(arr)) {
|
||||
return arr.slice(start, end);
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
coreModule.filter('stringify', function() {
|
||||
return function(arr) {
|
||||
coreModule.filter('stringify', () => {
|
||||
return arr => {
|
||||
if (_.isObject(arr) && !_.isArray(arr)) {
|
||||
return angular.toJson(arr);
|
||||
} else {
|
||||
@@ -27,8 +27,8 @@ coreModule.filter('stringify', function() {
|
||||
};
|
||||
});
|
||||
|
||||
coreModule.filter('moment', function() {
|
||||
return function(date, mode) {
|
||||
coreModule.filter('moment', () => {
|
||||
return (date, mode) => {
|
||||
switch (mode) {
|
||||
case 'ago':
|
||||
return moment(date).fromNow();
|
||||
@@ -37,8 +37,8 @@ coreModule.filter('moment', function() {
|
||||
};
|
||||
});
|
||||
|
||||
coreModule.filter('noXml', function() {
|
||||
const noXml = function(text) {
|
||||
coreModule.filter('noXml', () => {
|
||||
const noXml = text => {
|
||||
return _.isString(text)
|
||||
? text
|
||||
.replace(/&/g, '&')
|
||||
@@ -48,15 +48,15 @@ coreModule.filter('noXml', function() {
|
||||
.replace(/"/g, '"')
|
||||
: text;
|
||||
};
|
||||
return function(text) {
|
||||
return text => {
|
||||
return _.isArray(text) ? _.map(text, noXml) : noXml(text);
|
||||
};
|
||||
});
|
||||
|
||||
/** @ngInject */
|
||||
function interpolateTemplateVars(templateSrv) {
|
||||
const filterFunc: any = function(text, scope) {
|
||||
var scopedVars;
|
||||
const filterFunc: any = (text, scope) => {
|
||||
let scopedVars;
|
||||
if (scope.ctrl) {
|
||||
scopedVars = (scope.ctrl.panel || scope.ctrl.row).scopedVars;
|
||||
} else {
|
||||
|
||||
@@ -2,20 +2,19 @@ import $ from 'jquery';
|
||||
import angular from 'angular';
|
||||
import _ from 'lodash';
|
||||
|
||||
var $win = $(window);
|
||||
const $win = $(window);
|
||||
|
||||
$.fn.place_tt = (function() {
|
||||
var defaults = {
|
||||
$.fn.place_tt = (() => {
|
||||
const defaults = {
|
||||
offset: 5,
|
||||
};
|
||||
|
||||
return function(x, y, opts) {
|
||||
return function(this: any, x, y, opts) {
|
||||
opts = $.extend(true, {}, defaults, opts);
|
||||
|
||||
return this.each(function() {
|
||||
var $tooltip = $(this),
|
||||
width,
|
||||
height;
|
||||
return this.each(() => {
|
||||
const $tooltip = $(this);
|
||||
let width, height;
|
||||
|
||||
$tooltip.addClass('grafana-tooltip');
|
||||
|
||||
@@ -29,8 +28,8 @@ $.fn.place_tt = (function() {
|
||||
.invoke([
|
||||
'$compile',
|
||||
'$rootScope',
|
||||
function($compile, $rootScope) {
|
||||
var tmpScope = $rootScope.$new(true);
|
||||
($compile, $rootScope) => {
|
||||
const tmpScope = $rootScope.$new(true);
|
||||
_.extend(tmpScope, opts.scopeData);
|
||||
|
||||
$compile($tooltip)(tmpScope);
|
||||
|
||||
@@ -13,7 +13,7 @@ export class LiveSrv {
|
||||
}
|
||||
|
||||
getWebSocketUrl() {
|
||||
var l = window.location;
|
||||
const l = window.location;
|
||||
return (l.protocol === 'https:' ? 'wss://' : 'ws://') + l.host + config.appSubUrl + '/ws';
|
||||
}
|
||||
|
||||
@@ -66,7 +66,7 @@ export class LiveSrv {
|
||||
return;
|
||||
}
|
||||
|
||||
var observer = this.observers[message.stream];
|
||||
const observer = this.observers[message.stream];
|
||||
if (!observer) {
|
||||
this.removeObserver(message.stream, null);
|
||||
return;
|
||||
@@ -128,5 +128,5 @@ export class LiveSrv {
|
||||
}
|
||||
}
|
||||
|
||||
var instance = new LiveSrv();
|
||||
const instance = new LiveSrv();
|
||||
export { instance as liveSrv };
|
||||
|
||||
@@ -4,7 +4,7 @@ import _ from 'lodash';
|
||||
Mixins :)
|
||||
*/
|
||||
_.mixin({
|
||||
move: function(array, fromIndex, toIndex) {
|
||||
move: (array, fromIndex, toIndex) => {
|
||||
array.splice(toIndex, 0, array.splice(fromIndex, 1)[0]);
|
||||
return array;
|
||||
},
|
||||
|
||||
@@ -38,8 +38,8 @@ export class NavModelSrv {
|
||||
}
|
||||
|
||||
getNav(...args) {
|
||||
var children = this.navItems;
|
||||
var nav = new NavModel();
|
||||
let children = this.navItems;
|
||||
const nav = new NavModel();
|
||||
|
||||
for (const id of args) {
|
||||
// if its a number then it's the index to use for main
|
||||
@@ -69,7 +69,7 @@ export class NavModelSrv {
|
||||
}
|
||||
|
||||
getNotFoundNav() {
|
||||
var node = {
|
||||
const node = {
|
||||
text: 'Page not found',
|
||||
icon: 'fa fa-fw fa-warning',
|
||||
subTitle: '404 Error',
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
var templates = (<any>require).context('../', true, /\.html$/);
|
||||
templates.keys().forEach(function(key) {
|
||||
let templates = (require as any).context('../', true, /\.html$/);
|
||||
templates.keys().forEach(key => {
|
||||
templates(key);
|
||||
});
|
||||
|
||||
@@ -64,12 +64,12 @@ export class Profiler {
|
||||
console.log('Dashboard::Performance Total Watchers: ' + this.getTotalWatcherCount());
|
||||
console.log('Dashboard::Performance Total ScopeCount: ' + this.scopeCount);
|
||||
|
||||
var timeTaken = this.timings.lastPanelInitializedAt - this.timings.dashboardLoadStart;
|
||||
const timeTaken = this.timings.lastPanelInitializedAt - this.timings.dashboardLoadStart;
|
||||
console.log('Dashboard::Performance All panels initialized in ' + timeTaken + ' ms');
|
||||
|
||||
// measure digest performance
|
||||
var rootDigestStart = window.performance.now();
|
||||
for (var i = 0; i < 30; i++) {
|
||||
const rootDigestStart = window.performance.now();
|
||||
for (let i = 0; i < 30; i++) {
|
||||
this.$rootScope.$apply();
|
||||
}
|
||||
|
||||
@@ -78,19 +78,19 @@ export class Profiler {
|
||||
}
|
||||
|
||||
getTotalWatcherCount() {
|
||||
var count = 0;
|
||||
var scopes = 0;
|
||||
var root = $(document.getElementsByTagName('body'));
|
||||
let count = 0;
|
||||
let scopes = 0;
|
||||
const root = $(document.getElementsByTagName('body'));
|
||||
|
||||
var f = function(element) {
|
||||
const f = element => {
|
||||
if (element.data().hasOwnProperty('$scope')) {
|
||||
scopes++;
|
||||
angular.forEach(element.data().$scope.$$watchers, function() {
|
||||
angular.forEach(element.data().$scope.$$watchers, () => {
|
||||
count++;
|
||||
});
|
||||
}
|
||||
|
||||
angular.forEach(element.children(), function(childElement) {
|
||||
angular.forEach(element.children(), childElement => {
|
||||
f($(childElement));
|
||||
});
|
||||
};
|
||||
@@ -126,5 +126,5 @@ export class Profiler {
|
||||
}
|
||||
}
|
||||
|
||||
var profiler = new Profiler();
|
||||
const profiler = new Profiler();
|
||||
export { profiler };
|
||||
|
||||
@@ -70,7 +70,7 @@ export class AlertSrv {
|
||||
const newAlertJson = angular.toJson(newAlert);
|
||||
|
||||
// remove same alert if it already exists
|
||||
_.remove(this.list, function(value) {
|
||||
_.remove(this.list, value => {
|
||||
return angular.toJson(value) === newAlertJson;
|
||||
});
|
||||
|
||||
|
||||
@@ -12,13 +12,14 @@ export class Analytics {
|
||||
dataType: 'script',
|
||||
cache: true,
|
||||
});
|
||||
const ga = ((<any>window).ga =
|
||||
(<any>window).ga ||
|
||||
const ga = ((window as any).ga =
|
||||
(window as any).ga ||
|
||||
function() {
|
||||
//tslint:disable-line:only-arrow-functions
|
||||
(ga.q = ga.q || []).push(arguments);
|
||||
});
|
||||
ga.l = +new Date();
|
||||
ga('create', (<any>config).googleAnalyticsId, 'auto');
|
||||
ga('create', (config as any).googleAnalyticsId, 'auto');
|
||||
ga('set', 'anonymizeIp', true);
|
||||
return ga;
|
||||
}
|
||||
@@ -26,7 +27,7 @@ export class Analytics {
|
||||
init() {
|
||||
this.$rootScope.$on('$viewContentLoaded', () => {
|
||||
const track = { page: this.$location.url() };
|
||||
const ga = (<any>window).ga || this.gaInit();
|
||||
const ga = (window as any).ga || this.gaInit();
|
||||
ga('set', track);
|
||||
ga('send', 'pageview');
|
||||
});
|
||||
@@ -35,7 +36,7 @@ export class Analytics {
|
||||
|
||||
/** @ngInject */
|
||||
function startAnalytics(googleAnalyticsSrv) {
|
||||
if ((<any>config).googleAnalyticsId) {
|
||||
if ((config as any).googleAnalyticsId) {
|
||||
googleAnalyticsSrv.init();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,7 +43,7 @@ export class BackendSrv {
|
||||
return;
|
||||
}
|
||||
|
||||
var data = err.data || { message: 'Unexpected error' };
|
||||
let data = err.data || { message: 'Unexpected error' };
|
||||
if (_.isString(data)) {
|
||||
data = { message: data };
|
||||
}
|
||||
@@ -74,8 +74,8 @@ export class BackendSrv {
|
||||
|
||||
request(options) {
|
||||
options.retry = options.retry || 0;
|
||||
var requestIsLocal = !options.url.match(/^http/);
|
||||
var firstAttempt = options.retry === 0;
|
||||
const requestIsLocal = !options.url.match(/^http/);
|
||||
const firstAttempt = options.retry === 0;
|
||||
|
||||
if (requestIsLocal) {
|
||||
if (this.contextSrv.user && this.contextSrv.user.orgId) {
|
||||
@@ -123,30 +123,31 @@ export class BackendSrv {
|
||||
}
|
||||
|
||||
resolveCancelerIfExists(requestId) {
|
||||
var cancelers = this.inFlightRequests[requestId];
|
||||
const cancelers = this.inFlightRequests[requestId];
|
||||
if (!_.isUndefined(cancelers) && cancelers.length) {
|
||||
cancelers[0].resolve();
|
||||
}
|
||||
}
|
||||
|
||||
datasourceRequest(options) {
|
||||
let canceler = null;
|
||||
options.retry = options.retry || 0;
|
||||
|
||||
// A requestID is provided by the datasource as a unique identifier for a
|
||||
// particular query. If the requestID exists, the promise it is keyed to
|
||||
// is canceled, canceling the previous datasource request if it is still
|
||||
// in-flight.
|
||||
var requestId = options.requestId;
|
||||
const requestId = options.requestId;
|
||||
if (requestId) {
|
||||
this.resolveCancelerIfExists(requestId);
|
||||
// create new canceler
|
||||
var canceler = this.$q.defer();
|
||||
canceler = this.$q.defer();
|
||||
options.timeout = canceler.promise;
|
||||
this.addCanceler(requestId, canceler);
|
||||
}
|
||||
|
||||
var requestIsLocal = !options.url.match(/^http/);
|
||||
var firstAttempt = options.retry === 0;
|
||||
const requestIsLocal = !options.url.match(/^http/);
|
||||
const firstAttempt = options.retry === 0;
|
||||
|
||||
if (requestIsLocal) {
|
||||
if (this.contextSrv.user && this.contextSrv.user.orgId) {
|
||||
|
||||
@@ -8,6 +8,8 @@ export class User {
|
||||
isSignedIn: any;
|
||||
orgRole: any;
|
||||
orgId: number;
|
||||
orgName: string;
|
||||
orgCount: number;
|
||||
timezone: string;
|
||||
helpFlags1: number;
|
||||
lightTheme: boolean;
|
||||
@@ -59,9 +61,9 @@ export class ContextSrv {
|
||||
}
|
||||
}
|
||||
|
||||
var contextSrv = new ContextSrv();
|
||||
const contextSrv = new ContextSrv();
|
||||
export { contextSrv };
|
||||
|
||||
coreModule.factory('contextSrv', function() {
|
||||
coreModule.factory('contextSrv', () => {
|
||||
return contextSrv;
|
||||
});
|
||||
|
||||
@@ -6,8 +6,8 @@ export class ImpressionSrv {
|
||||
constructor() {}
|
||||
|
||||
addDashboardImpression(dashboardId) {
|
||||
var impressionsKey = this.impressionKey(config);
|
||||
var impressions = [];
|
||||
const impressionsKey = this.impressionKey(config);
|
||||
let impressions = [];
|
||||
if (store.exists(impressionsKey)) {
|
||||
impressions = JSON.parse(store.get(impressionsKey));
|
||||
if (!_.isArray(impressions)) {
|
||||
@@ -28,7 +28,7 @@ export class ImpressionSrv {
|
||||
}
|
||||
|
||||
getDashboardOpened() {
|
||||
var impressions = store.get(this.impressionKey(config)) || '[]';
|
||||
let impressions = store.get(this.impressionKey(config)) || '[]';
|
||||
|
||||
impressions = JSON.parse(impressions);
|
||||
|
||||
|
||||
@@ -77,15 +77,15 @@ export class KeybindingSrv {
|
||||
|
||||
appEvents.emit('hide-modal');
|
||||
|
||||
if (!this.modalOpen) {
|
||||
if (this.timepickerOpen) {
|
||||
this.$rootScope.appEvent('closeTimepicker');
|
||||
this.timepickerOpen = false;
|
||||
} else {
|
||||
this.$rootScope.appEvent('panel-change-view', { fullscreen: false, edit: false });
|
||||
}
|
||||
} else {
|
||||
if (this.modalOpen) {
|
||||
this.modalOpen = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.timepickerOpen) {
|
||||
this.$rootScope.appEvent('closeTimepicker');
|
||||
this.timepickerOpen = false;
|
||||
return;
|
||||
}
|
||||
|
||||
// close settings view
|
||||
@@ -93,6 +93,16 @@ export class KeybindingSrv {
|
||||
if (search.editview) {
|
||||
delete search.editview;
|
||||
this.$location.search(search);
|
||||
return;
|
||||
}
|
||||
|
||||
if (search.fullscreen) {
|
||||
this.$rootScope.appEvent('panel-change-view', { fullscreen: false, edit: false });
|
||||
return;
|
||||
}
|
||||
|
||||
if (search.kiosk) {
|
||||
this.$rootScope.appEvent('toggle-kiosk-mode', { exit: true });
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -27,14 +27,14 @@ function getReactComponent(name, $injector) {
|
||||
}
|
||||
|
||||
// ensure the specified React component is accessible, and fail fast if it's not
|
||||
var reactComponent;
|
||||
let reactComponent;
|
||||
try {
|
||||
reactComponent = $injector.get(name);
|
||||
} catch (e) {}
|
||||
|
||||
if (!reactComponent) {
|
||||
try {
|
||||
reactComponent = name.split('.').reduce(function(current, namePart) {
|
||||
reactComponent = name.split('.').reduce((current, namePart) => {
|
||||
return current[namePart];
|
||||
}, window);
|
||||
} catch (e) {}
|
||||
@@ -52,13 +52,14 @@ function applied(fn, scope) {
|
||||
if (fn.wrappedInApply) {
|
||||
return fn;
|
||||
}
|
||||
var wrapped: any = function() {
|
||||
var args = arguments;
|
||||
var phase = scope.$root.$$phase;
|
||||
const wrapped: any = function() {
|
||||
//tslint:disable-line:only-arrow-functions
|
||||
const args = arguments;
|
||||
const phase = scope.$root.$$phase;
|
||||
if (phase === '$apply' || phase === '$digest') {
|
||||
return fn.apply(null, args);
|
||||
} else {
|
||||
return scope.$apply(function() {
|
||||
return scope.$apply(() => {
|
||||
return fn.apply(null, args);
|
||||
});
|
||||
}
|
||||
@@ -80,9 +81,9 @@ function applied(fn, scope) {
|
||||
* @returns {Object} props with the functions wrapped in scope.$apply
|
||||
*/
|
||||
function applyFunctions(obj, scope, propsConfig?) {
|
||||
return Object.keys(obj || {}).reduce(function(prev, key) {
|
||||
var value = obj[key];
|
||||
var config = (propsConfig || {})[key] || {};
|
||||
return Object.keys(obj || {}).reduce((prev, key) => {
|
||||
const value = obj[key];
|
||||
const config = (propsConfig || {})[key] || {};
|
||||
/**
|
||||
* wrap functions in a function that ensures they are scope.$applied
|
||||
* ensures that when function is called from a React component
|
||||
@@ -103,14 +104,14 @@ function applyFunctions(obj, scope, propsConfig?) {
|
||||
* If watchDepth attribute is NOT reference or collection, watchDepth defaults to deep watching by value
|
||||
*/
|
||||
function watchProps(watchDepth, scope, watchExpressions, listener) {
|
||||
var supportsWatchCollection = angular.isFunction(scope.$watchCollection);
|
||||
var supportsWatchGroup = angular.isFunction(scope.$watchGroup);
|
||||
const supportsWatchCollection = angular.isFunction(scope.$watchCollection);
|
||||
const supportsWatchGroup = angular.isFunction(scope.$watchGroup);
|
||||
|
||||
var watchGroupExpressions = [];
|
||||
const watchGroupExpressions = [];
|
||||
|
||||
watchExpressions.forEach(function(expr) {
|
||||
var actualExpr = getPropExpression(expr);
|
||||
var exprWatchDepth = getPropWatchDepth(watchDepth, expr);
|
||||
watchExpressions.forEach(expr => {
|
||||
const actualExpr = getPropExpression(expr);
|
||||
const exprWatchDepth = getPropWatchDepth(watchDepth, expr);
|
||||
|
||||
if (exprWatchDepth === 'collection' && supportsWatchCollection) {
|
||||
scope.$watchCollection(actualExpr, listener);
|
||||
@@ -134,7 +135,7 @@ function watchProps(watchDepth, scope, watchExpressions, listener) {
|
||||
|
||||
// render React component, with scope[attrs.props] being passed in as the component props
|
||||
function renderComponent(component, props, scope, elem) {
|
||||
scope.$evalAsync(function() {
|
||||
scope.$evalAsync(() => {
|
||||
ReactDOM.render(React.createElement(component, props), elem[0]);
|
||||
});
|
||||
}
|
||||
@@ -156,7 +157,7 @@ function getPropExpression(prop) {
|
||||
|
||||
// find the normalized attribute knowing that React props accept any type of capitalization
|
||||
function findAttribute(attrs, propName) {
|
||||
var index = Object.keys(attrs).filter(function(attr) {
|
||||
const index = Object.keys(attrs).filter(attr => {
|
||||
return attr.toLowerCase() === propName.toLowerCase();
|
||||
})[0];
|
||||
return attrs[index];
|
||||
@@ -164,7 +165,7 @@ function findAttribute(attrs, propName) {
|
||||
|
||||
// get watch depth of prop (string or array)
|
||||
function getPropWatchDepth(defaultWatch, prop) {
|
||||
var customWatchDepth = Array.isArray(prop) && angular.isObject(prop[1]) && prop[1].watchDepth;
|
||||
const customWatchDepth = Array.isArray(prop) && angular.isObject(prop[1]) && prop[1].watchDepth;
|
||||
return customWatchDepth || defaultWatch;
|
||||
}
|
||||
|
||||
@@ -186,16 +187,16 @@ function getPropWatchDepth(defaultWatch, prop) {
|
||||
// }
|
||||
// }));
|
||||
//
|
||||
var reactComponent = function($injector) {
|
||||
const reactComponent = $injector => {
|
||||
return {
|
||||
restrict: 'E',
|
||||
replace: true,
|
||||
link: function(scope, elem, attrs) {
|
||||
var reactComponent = getReactComponent(attrs.name, $injector);
|
||||
const reactComponent = getReactComponent(attrs.name, $injector);
|
||||
|
||||
var renderMyComponent = function() {
|
||||
var scopeProps = scope.$eval(attrs.props);
|
||||
var props = applyFunctions(scopeProps, scope);
|
||||
const renderMyComponent = () => {
|
||||
const scopeProps = scope.$eval(attrs.props);
|
||||
const props = applyFunctions(scopeProps, scope);
|
||||
|
||||
renderComponent(reactComponent, props, scope, elem);
|
||||
};
|
||||
@@ -204,7 +205,7 @@ var reactComponent = function($injector) {
|
||||
attrs.props ? watchProps(attrs.watchDepth, scope, [attrs.props], renderMyComponent) : renderMyComponent();
|
||||
|
||||
// cleanup when scope is destroyed
|
||||
scope.$on('$destroy', function() {
|
||||
scope.$on('$destroy', () => {
|
||||
if (!attrs.onScopeDestroy) {
|
||||
ReactDOM.unmountComponentAtNode(elem[0]);
|
||||
} else {
|
||||
@@ -243,24 +244,24 @@ var reactComponent = function($injector) {
|
||||
//
|
||||
// <hello name="name"/>
|
||||
//
|
||||
var reactDirective = function($injector) {
|
||||
return function(reactComponentName, props, conf, injectableProps) {
|
||||
var directive = {
|
||||
const reactDirective = $injector => {
|
||||
return (reactComponentName, props, conf, injectableProps) => {
|
||||
const directive = {
|
||||
restrict: 'E',
|
||||
replace: true,
|
||||
link: function(scope, elem, attrs) {
|
||||
var reactComponent = getReactComponent(reactComponentName, $injector);
|
||||
const reactComponent = getReactComponent(reactComponentName, $injector);
|
||||
|
||||
// if props is not defined, fall back to use the React component's propTypes if present
|
||||
props = props || Object.keys(reactComponent.propTypes || {});
|
||||
|
||||
// for each of the properties, get their scope value and set it to scope.props
|
||||
var renderMyComponent = function() {
|
||||
var scopeProps = {},
|
||||
config = {};
|
||||
const renderMyComponent = () => {
|
||||
let scopeProps = {};
|
||||
const config = {};
|
||||
|
||||
props.forEach(function(prop) {
|
||||
var propName = getPropName(prop);
|
||||
props.forEach(prop => {
|
||||
const propName = getPropName(prop);
|
||||
scopeProps[propName] = scope.$eval(findAttribute(attrs, propName));
|
||||
config[propName] = getPropConfig(prop);
|
||||
});
|
||||
@@ -272,7 +273,7 @@ var reactDirective = function($injector) {
|
||||
|
||||
// watch each property name and trigger an update whenever something changes,
|
||||
// to update scope.props with new values
|
||||
var propExpressions = props.map(function(prop) {
|
||||
const propExpressions = props.map(prop => {
|
||||
return Array.isArray(prop) ? [attrs[getPropName(prop)], getPropConfig(prop)] : attrs[prop];
|
||||
});
|
||||
|
||||
@@ -280,7 +281,7 @@ var reactDirective = function($injector) {
|
||||
props.length ? watchProps(attrs.watchDepth, scope, propExpressions, renderMyComponent) : renderMyComponent();
|
||||
|
||||
// cleanup when scope is destroyed
|
||||
scope.$on('$destroy', function() {
|
||||
scope.$on('$destroy', () => {
|
||||
if (!attrs.onScopeDestroy) {
|
||||
ReactDOM.unmountComponentAtNode(elem[0]);
|
||||
} else {
|
||||
|
||||
@@ -2,17 +2,17 @@ import _ from 'lodash';
|
||||
import coreModule from 'app/core/core_module';
|
||||
import Drop from 'tether-drop';
|
||||
|
||||
/** @ngInject **/
|
||||
function popoverSrv($compile, $rootScope, $timeout) {
|
||||
/** @ngInject */
|
||||
function popoverSrv(this: any, $compile, $rootScope, $timeout) {
|
||||
let openDrop = null;
|
||||
|
||||
this.close = function() {
|
||||
this.close = () => {
|
||||
if (openDrop) {
|
||||
openDrop.close();
|
||||
}
|
||||
};
|
||||
|
||||
this.show = function(options) {
|
||||
this.show = options => {
|
||||
if (openDrop) {
|
||||
openDrop.close();
|
||||
openDrop = null;
|
||||
@@ -68,7 +68,7 @@ function popoverSrv($compile, $rootScope, $timeout) {
|
||||
}, 100);
|
||||
|
||||
// return close function
|
||||
return function() {
|
||||
return () => {
|
||||
if (drop) {
|
||||
drop.close();
|
||||
}
|
||||
|
||||
@@ -31,7 +31,7 @@ export class SearchSrv {
|
||||
}
|
||||
|
||||
private queryForRecentDashboards() {
|
||||
var dashIds = _.take(impressionSrv.getDashboardOpened(), 5);
|
||||
const dashIds = _.take(impressionSrv.getDashboardOpened(), 5);
|
||||
if (dashIds.length === 0) {
|
||||
return Promise.resolve([]);
|
||||
}
|
||||
|
||||
@@ -2,10 +2,10 @@ import _ from 'lodash';
|
||||
import coreModule from '../core_module';
|
||||
|
||||
/** @ngInject */
|
||||
export function uiSegmentSrv($sce, templateSrv) {
|
||||
export function uiSegmentSrv(this: any, $sce, templateSrv) {
|
||||
const self = this;
|
||||
|
||||
function MetricSegment(options) {
|
||||
function MetricSegment(this: any, options) {
|
||||
if (options === '*' || options.value === '*') {
|
||||
this.value = '*';
|
||||
this.html = $sce.trustAsHtml('<i class="fa fa-asterisk"><i>');
|
||||
@@ -42,48 +42,48 @@ export function uiSegmentSrv($sce, templateSrv) {
|
||||
}
|
||||
};
|
||||
|
||||
this.newSelectMeasurement = function() {
|
||||
this.newSelectMeasurement = () => {
|
||||
return new MetricSegment({ value: 'select measurement', fake: true });
|
||||
};
|
||||
|
||||
this.newFake = function(text, type, cssClass) {
|
||||
this.newFake = (text, type, cssClass) => {
|
||||
return new MetricSegment({ value: text, fake: true, type: type, cssClass: cssClass });
|
||||
};
|
||||
|
||||
this.newSegment = function(options) {
|
||||
this.newSegment = options => {
|
||||
return new MetricSegment(options);
|
||||
};
|
||||
|
||||
this.newKey = function(key) {
|
||||
this.newKey = key => {
|
||||
return new MetricSegment({ value: key, type: 'key', cssClass: 'query-segment-key' });
|
||||
};
|
||||
|
||||
this.newKeyValue = function(value) {
|
||||
this.newKeyValue = value => {
|
||||
return new MetricSegment({ value: value, type: 'value', cssClass: 'query-segment-value' });
|
||||
};
|
||||
|
||||
this.newCondition = function(condition) {
|
||||
this.newCondition = condition => {
|
||||
return new MetricSegment({ value: condition, type: 'condition', cssClass: 'query-keyword' });
|
||||
};
|
||||
|
||||
this.newOperator = function(op) {
|
||||
this.newOperator = op => {
|
||||
return new MetricSegment({ value: op, type: 'operator', cssClass: 'query-segment-operator' });
|
||||
};
|
||||
|
||||
this.newOperators = function(ops) {
|
||||
return _.map(ops, function(op) {
|
||||
this.newOperators = ops => {
|
||||
return _.map(ops, op => {
|
||||
return new MetricSegment({ value: op, type: 'operator', cssClass: 'query-segment-operator' });
|
||||
});
|
||||
};
|
||||
|
||||
this.transformToSegments = function(addTemplateVars, variableTypeFilter) {
|
||||
return function(results) {
|
||||
const segments = _.map(results, function(segment) {
|
||||
this.transformToSegments = (addTemplateVars, variableTypeFilter) => {
|
||||
return results => {
|
||||
const segments = _.map(results, segment => {
|
||||
return self.newSegment({ value: segment.text, expandable: segment.expandable });
|
||||
});
|
||||
|
||||
if (addTemplateVars) {
|
||||
_.each(templateSrv.variables, function(variable) {
|
||||
_.each(templateSrv.variables, variable => {
|
||||
if (variableTypeFilter === void 0 || variableTypeFilter === variable.type) {
|
||||
segments.unshift(self.newSegment({ type: 'value', value: '$' + variable.name, expandable: true }));
|
||||
}
|
||||
@@ -94,11 +94,11 @@ export function uiSegmentSrv($sce, templateSrv) {
|
||||
};
|
||||
};
|
||||
|
||||
this.newSelectMetric = function() {
|
||||
this.newSelectMetric = () => {
|
||||
return new MetricSegment({ value: 'select metric', fake: true });
|
||||
};
|
||||
|
||||
this.newPlusButton = function() {
|
||||
this.newPlusButton = () => {
|
||||
return new MetricSegment({
|
||||
fake: true,
|
||||
html: '<i class="fa fa-plus "></i>',
|
||||
|
||||
@@ -44,7 +44,7 @@ export class UtilSrv {
|
||||
backdrop: options.backdrop,
|
||||
});
|
||||
|
||||
Promise.resolve(modal).then(function(modalEl) {
|
||||
Promise.resolve(modal).then(modalEl => {
|
||||
modalEl.modal('show');
|
||||
});
|
||||
}
|
||||
@@ -52,12 +52,12 @@ export class UtilSrv {
|
||||
showConfirmModal(payload) {
|
||||
const scope = this.$rootScope.$new();
|
||||
|
||||
scope.onConfirm = function() {
|
||||
scope.onConfirm = () => {
|
||||
payload.onConfirm();
|
||||
scope.dismiss();
|
||||
};
|
||||
|
||||
scope.updateConfirmText = function(value) {
|
||||
scope.updateConfirmText = value => {
|
||||
scope.confirmTextValid = payload.confirmText.toLowerCase() === value.toLowerCase();
|
||||
};
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { BackendSrv } from 'app/core/services/backend_srv';
|
||||
jest.mock('app/core/store');
|
||||
|
||||
describe('backend_srv', function() {
|
||||
describe('backend_srv', () => {
|
||||
const _httpBackend = options => {
|
||||
if (options.url === 'gateway-error') {
|
||||
return Promise.reject({ status: 502 });
|
||||
|
||||
@@ -55,8 +55,8 @@ describe('DateMath', () => {
|
||||
});
|
||||
|
||||
describe('subtraction', () => {
|
||||
var now;
|
||||
var anchored;
|
||||
let now;
|
||||
let anchored;
|
||||
|
||||
beforeEach(() => {
|
||||
clock = sinon.useFakeTimers(unix);
|
||||
@@ -83,7 +83,7 @@ describe('DateMath', () => {
|
||||
});
|
||||
|
||||
describe('rounding', () => {
|
||||
var now;
|
||||
let now;
|
||||
|
||||
beforeEach(() => {
|
||||
clock = sinon.useFakeTimers(unix);
|
||||
@@ -91,11 +91,11 @@ describe('DateMath', () => {
|
||||
});
|
||||
|
||||
_.each(spans, span => {
|
||||
it('should round now to the beginning of the ' + span, function() {
|
||||
it('should round now to the beginning of the ' + span, () => {
|
||||
expect(dateMath.parse('now/' + span).format(format)).toEqual(now.startOf(span).format(format));
|
||||
});
|
||||
|
||||
it('should round now to the end of the ' + span, function() {
|
||||
it('should round now to the end of the ' + span, () => {
|
||||
expect(dateMath.parse('now/' + span, true).format(format)).toEqual(now.endOf(span).format(format));
|
||||
});
|
||||
});
|
||||
@@ -114,18 +114,18 @@ describe('DateMath', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('relative time to date parsing', function() {
|
||||
it('should handle negative time', function() {
|
||||
describe('relative time to date parsing', () => {
|
||||
it('should handle negative time', () => {
|
||||
const date = dateMath.parseDateMath('-2d', moment([2014, 1, 5]));
|
||||
expect(date.valueOf()).toEqual(moment([2014, 1, 3]).valueOf());
|
||||
});
|
||||
|
||||
it('should handle multiple math expressions', function() {
|
||||
it('should handle multiple math expressions', () => {
|
||||
const date = dateMath.parseDateMath('-2d-6h', moment([2014, 1, 5]));
|
||||
expect(date.valueOf()).toEqual(moment([2014, 1, 2, 18]).valueOf());
|
||||
});
|
||||
|
||||
it('should return false when invalid expression', function() {
|
||||
it('should return false when invalid expression', () => {
|
||||
const date = dateMath.parseDateMath('2', moment([2014, 1, 5]));
|
||||
expect(date).toEqual(undefined);
|
||||
});
|
||||
|
||||
@@ -101,7 +101,7 @@ describe('file_export', () => {
|
||||
expect(returnedText).toBe(expectedText);
|
||||
});
|
||||
|
||||
it('should decode HTML encoded characters', function() {
|
||||
it('should decode HTML encoded characters', () => {
|
||||
const inputTable = {
|
||||
columns: [{ text: 'string_value' }],
|
||||
rows: [
|
||||
|
||||
@@ -2,27 +2,27 @@ import kbn from '../utils/kbn';
|
||||
import * as dateMath from '../utils/datemath';
|
||||
import moment from 'moment';
|
||||
|
||||
describe('unit format menu', function() {
|
||||
describe('unit format menu', () => {
|
||||
const menu = kbn.getUnitFormats();
|
||||
menu.map(function(submenu) {
|
||||
describe('submenu ' + submenu.text, function() {
|
||||
it('should have a title', function() {
|
||||
menu.map(submenu => {
|
||||
describe('submenu ' + submenu.text, () => {
|
||||
it('should have a title', () => {
|
||||
expect(typeof submenu.text).toBe('string');
|
||||
});
|
||||
|
||||
it('should have a submenu', function() {
|
||||
it('should have a submenu', () => {
|
||||
expect(Array.isArray(submenu.submenu)).toBe(true);
|
||||
});
|
||||
|
||||
submenu.submenu.map(function(entry) {
|
||||
describe('entry ' + entry.text, function() {
|
||||
it('should have a title', function() {
|
||||
submenu.submenu.map(entry => {
|
||||
describe('entry ' + entry.text, () => {
|
||||
it('should have a title', () => {
|
||||
expect(typeof entry.text).toBe('string');
|
||||
});
|
||||
it('should have a format', function() {
|
||||
it('should have a format', () => {
|
||||
expect(typeof entry.value).toBe('string');
|
||||
});
|
||||
it('should have a valid format', function() {
|
||||
it('should have a valid format', () => {
|
||||
expect(typeof kbn.valueFormats[entry.value]).toBe('function');
|
||||
});
|
||||
});
|
||||
@@ -32,8 +32,8 @@ describe('unit format menu', function() {
|
||||
});
|
||||
|
||||
function describeValueFormat(desc, value, tickSize, tickDecimals, result) {
|
||||
describe('value format: ' + desc, function() {
|
||||
it('should translate ' + value + ' as ' + result, function() {
|
||||
describe('value format: ' + desc, () => {
|
||||
it('should translate ' + value + ' as ' + result, () => {
|
||||
const scaledDecimals = tickDecimals - Math.floor(Math.log(tickSize) / Math.LN10);
|
||||
const str = kbn.valueFormats[desc](value, tickDecimals, scaledDecimals);
|
||||
expect(str).toBe(result);
|
||||
@@ -100,85 +100,85 @@ describeValueFormat('d', 3, 1, 0, '3 day');
|
||||
describeValueFormat('d', 245, 100, 0, '35 week');
|
||||
describeValueFormat('d', 2456, 10, 0, '6.73 year');
|
||||
|
||||
describe('date time formats', function() {
|
||||
describe('date time formats', () => {
|
||||
const epoch = 1505634997920;
|
||||
const utcTime = moment.utc(epoch);
|
||||
const browserTime = moment(epoch);
|
||||
|
||||
it('should format as iso date', function() {
|
||||
it('should format as iso date', () => {
|
||||
const expected = browserTime.format('YYYY-MM-DD HH:mm:ss');
|
||||
const actual = kbn.valueFormats.dateTimeAsIso(epoch);
|
||||
expect(actual).toBe(expected);
|
||||
});
|
||||
|
||||
it('should format as iso date (in UTC)', function() {
|
||||
it('should format as iso date (in UTC)', () => {
|
||||
const expected = utcTime.format('YYYY-MM-DD HH:mm:ss');
|
||||
const actual = kbn.valueFormats.dateTimeAsIso(epoch, true);
|
||||
expect(actual).toBe(expected);
|
||||
});
|
||||
|
||||
it('should format as iso date and skip date when today', function() {
|
||||
it('should format as iso date and skip date when today', () => {
|
||||
const now = moment();
|
||||
const expected = now.format('HH:mm:ss');
|
||||
const actual = kbn.valueFormats.dateTimeAsIso(now.valueOf(), false);
|
||||
expect(actual).toBe(expected);
|
||||
});
|
||||
|
||||
it('should format as iso date (in UTC) and skip date when today', function() {
|
||||
it('should format as iso date (in UTC) and skip date when today', () => {
|
||||
const now = moment.utc();
|
||||
const expected = now.format('HH:mm:ss');
|
||||
const actual = kbn.valueFormats.dateTimeAsIso(now.valueOf(), true);
|
||||
expect(actual).toBe(expected);
|
||||
});
|
||||
|
||||
it('should format as US date', function() {
|
||||
it('should format as US date', () => {
|
||||
const expected = browserTime.format('MM/DD/YYYY h:mm:ss a');
|
||||
const actual = kbn.valueFormats.dateTimeAsUS(epoch, false);
|
||||
expect(actual).toBe(expected);
|
||||
});
|
||||
|
||||
it('should format as US date (in UTC)', function() {
|
||||
it('should format as US date (in UTC)', () => {
|
||||
const expected = utcTime.format('MM/DD/YYYY h:mm:ss a');
|
||||
const actual = kbn.valueFormats.dateTimeAsUS(epoch, true);
|
||||
expect(actual).toBe(expected);
|
||||
});
|
||||
|
||||
it('should format as US date and skip date when today', function() {
|
||||
it('should format as US date and skip date when today', () => {
|
||||
const now = moment();
|
||||
const expected = now.format('h:mm:ss a');
|
||||
const actual = kbn.valueFormats.dateTimeAsUS(now.valueOf(), false);
|
||||
expect(actual).toBe(expected);
|
||||
});
|
||||
|
||||
it('should format as US date (in UTC) and skip date when today', function() {
|
||||
it('should format as US date (in UTC) and skip date when today', () => {
|
||||
const now = moment.utc();
|
||||
const expected = now.format('h:mm:ss a');
|
||||
const actual = kbn.valueFormats.dateTimeAsUS(now.valueOf(), true);
|
||||
expect(actual).toBe(expected);
|
||||
});
|
||||
|
||||
it('should format as from now with days', function() {
|
||||
it('should format as from now with days', () => {
|
||||
const daysAgo = moment().add(-7, 'd');
|
||||
const expected = '7 days ago';
|
||||
const actual = kbn.valueFormats.dateTimeFromNow(daysAgo.valueOf(), false);
|
||||
expect(actual).toBe(expected);
|
||||
});
|
||||
|
||||
it('should format as from now with days (in UTC)', function() {
|
||||
it('should format as from now with days (in UTC)', () => {
|
||||
const daysAgo = moment.utc().add(-7, 'd');
|
||||
const expected = '7 days ago';
|
||||
const actual = kbn.valueFormats.dateTimeFromNow(daysAgo.valueOf(), true);
|
||||
expect(actual).toBe(expected);
|
||||
});
|
||||
|
||||
it('should format as from now with minutes', function() {
|
||||
it('should format as from now with minutes', () => {
|
||||
const daysAgo = moment().add(-2, 'm');
|
||||
const expected = '2 minutes ago';
|
||||
const actual = kbn.valueFormats.dateTimeFromNow(daysAgo.valueOf(), false);
|
||||
expect(actual).toBe(expected);
|
||||
});
|
||||
|
||||
it('should format as from now with minutes (in UTC)', function() {
|
||||
it('should format as from now with minutes (in UTC)', () => {
|
||||
const daysAgo = moment.utc().add(-2, 'm');
|
||||
const expected = '2 minutes ago';
|
||||
const actual = kbn.valueFormats.dateTimeFromNow(daysAgo.valueOf(), true);
|
||||
@@ -186,92 +186,92 @@ describe('date time formats', function() {
|
||||
});
|
||||
});
|
||||
|
||||
describe('kbn.toFixed and negative decimals', function() {
|
||||
it('should treat as zero decimals', function() {
|
||||
describe('kbn.toFixed and negative decimals', () => {
|
||||
it('should treat as zero decimals', () => {
|
||||
const str = kbn.toFixed(186.123, -2);
|
||||
expect(str).toBe('186');
|
||||
});
|
||||
});
|
||||
|
||||
describe('kbn ms format when scaled decimals is null do not use it', function() {
|
||||
it('should use specified decimals', function() {
|
||||
describe('kbn ms format when scaled decimals is null do not use it', () => {
|
||||
it('should use specified decimals', () => {
|
||||
const str = kbn.valueFormats['ms'](10000086.123, 1, null);
|
||||
expect(str).toBe('2.8 hour');
|
||||
});
|
||||
});
|
||||
|
||||
describe('kbn kbytes format when scaled decimals is null do not use it', function() {
|
||||
it('should use specified decimals', function() {
|
||||
describe('kbn kbytes format when scaled decimals is null do not use it', () => {
|
||||
it('should use specified decimals', () => {
|
||||
const str = kbn.valueFormats['kbytes'](10000000, 3, null);
|
||||
expect(str).toBe('9.537 GiB');
|
||||
});
|
||||
});
|
||||
|
||||
describe('kbn deckbytes format when scaled decimals is null do not use it', function() {
|
||||
it('should use specified decimals', function() {
|
||||
describe('kbn deckbytes format when scaled decimals is null do not use it', () => {
|
||||
it('should use specified decimals', () => {
|
||||
const str = kbn.valueFormats['deckbytes'](10000000, 3, null);
|
||||
expect(str).toBe('10.000 GB');
|
||||
});
|
||||
});
|
||||
|
||||
describe('kbn roundValue', function() {
|
||||
it('should should handle null value', function() {
|
||||
describe('kbn roundValue', () => {
|
||||
it('should should handle null value', () => {
|
||||
const str = kbn.roundValue(null, 2);
|
||||
expect(str).toBe(null);
|
||||
});
|
||||
it('should round value', function() {
|
||||
it('should round value', () => {
|
||||
const str = kbn.roundValue(200.877, 2);
|
||||
expect(str).toBe(200.88);
|
||||
});
|
||||
});
|
||||
|
||||
describe('calculateInterval', function() {
|
||||
it('1h 100 resultion', function() {
|
||||
describe('calculateInterval', () => {
|
||||
it('1h 100 resultion', () => {
|
||||
const range = { from: dateMath.parse('now-1h'), to: dateMath.parse('now') };
|
||||
const res = kbn.calculateInterval(range, 100, null);
|
||||
expect(res.interval).toBe('30s');
|
||||
});
|
||||
|
||||
it('10m 1600 resolution', function() {
|
||||
it('10m 1600 resolution', () => {
|
||||
const range = { from: dateMath.parse('now-10m'), to: dateMath.parse('now') };
|
||||
const res = kbn.calculateInterval(range, 1600, null);
|
||||
expect(res.interval).toBe('500ms');
|
||||
expect(res.intervalMs).toBe(500);
|
||||
});
|
||||
|
||||
it('fixed user min interval', function() {
|
||||
it('fixed user min interval', () => {
|
||||
const range = { from: dateMath.parse('now-10m'), to: dateMath.parse('now') };
|
||||
const res = kbn.calculateInterval(range, 1600, '10s');
|
||||
expect(res.interval).toBe('10s');
|
||||
expect(res.intervalMs).toBe(10000);
|
||||
});
|
||||
|
||||
it('short time range and user low limit', function() {
|
||||
it('short time range and user low limit', () => {
|
||||
const range = { from: dateMath.parse('now-10m'), to: dateMath.parse('now') };
|
||||
const res = kbn.calculateInterval(range, 1600, '>10s');
|
||||
expect(res.interval).toBe('10s');
|
||||
});
|
||||
|
||||
it('large time range and user low limit', function() {
|
||||
it('large time range and user low limit', () => {
|
||||
const range = { from: dateMath.parse('now-14d'), to: dateMath.parse('now') };
|
||||
const res = kbn.calculateInterval(range, 1000, '>10s');
|
||||
expect(res.interval).toBe('20m');
|
||||
});
|
||||
|
||||
it('10s 900 resolution and user low limit in ms', function() {
|
||||
it('10s 900 resolution and user low limit in ms', () => {
|
||||
const range = { from: dateMath.parse('now-10s'), to: dateMath.parse('now') };
|
||||
const res = kbn.calculateInterval(range, 900, '>15ms');
|
||||
expect(res.interval).toBe('15ms');
|
||||
});
|
||||
|
||||
it('1d 1 resolution', function() {
|
||||
it('1d 1 resolution', () => {
|
||||
const range = { from: dateMath.parse('now-1d'), to: dateMath.parse('now') };
|
||||
const res = kbn.calculateInterval(range, 1, null);
|
||||
expect(res.interval).toBe('1d');
|
||||
expect(res.intervalMs).toBe(86400000);
|
||||
});
|
||||
|
||||
it('86399s 1 resolution', function() {
|
||||
it('86399s 1 resolution', () => {
|
||||
const range = {
|
||||
from: dateMath.parse('now-86390s'),
|
||||
to: dateMath.parse('now'),
|
||||
@@ -282,140 +282,140 @@ describe('calculateInterval', function() {
|
||||
});
|
||||
});
|
||||
|
||||
describe('hex', function() {
|
||||
it('positive integer', function() {
|
||||
describe('hex', () => {
|
||||
it('positive integer', () => {
|
||||
const str = kbn.valueFormats.hex(100, 0);
|
||||
expect(str).toBe('64');
|
||||
});
|
||||
it('negative integer', function() {
|
||||
it('negative integer', () => {
|
||||
const str = kbn.valueFormats.hex(-100, 0);
|
||||
expect(str).toBe('-64');
|
||||
});
|
||||
it('null', function() {
|
||||
it('null', () => {
|
||||
const str = kbn.valueFormats.hex(null, 0);
|
||||
expect(str).toBe('');
|
||||
});
|
||||
it('positive float', function() {
|
||||
it('positive float', () => {
|
||||
const str = kbn.valueFormats.hex(50.52, 1);
|
||||
expect(str).toBe('32.8');
|
||||
});
|
||||
it('negative float', function() {
|
||||
it('negative float', () => {
|
||||
const str = kbn.valueFormats.hex(-50.333, 2);
|
||||
expect(str).toBe('-32.547AE147AE14');
|
||||
});
|
||||
});
|
||||
|
||||
describe('hex 0x', function() {
|
||||
it('positive integeter', function() {
|
||||
describe('hex 0x', () => {
|
||||
it('positive integeter', () => {
|
||||
const str = kbn.valueFormats.hex0x(7999, 0);
|
||||
expect(str).toBe('0x1F3F');
|
||||
});
|
||||
it('negative integer', function() {
|
||||
it('negative integer', () => {
|
||||
const str = kbn.valueFormats.hex0x(-584, 0);
|
||||
expect(str).toBe('-0x248');
|
||||
});
|
||||
it('null', function() {
|
||||
it('null', () => {
|
||||
const str = kbn.valueFormats.hex0x(null, 0);
|
||||
expect(str).toBe('');
|
||||
});
|
||||
it('positive float', function() {
|
||||
it('positive float', () => {
|
||||
const str = kbn.valueFormats.hex0x(74.443, 3);
|
||||
expect(str).toBe('0x4A.716872B020C4');
|
||||
});
|
||||
it('negative float', function() {
|
||||
it('negative float', () => {
|
||||
const str = kbn.valueFormats.hex0x(-65.458, 1);
|
||||
expect(str).toBe('-0x41.8');
|
||||
});
|
||||
});
|
||||
|
||||
describe('duration', function() {
|
||||
it('null', function() {
|
||||
describe('duration', () => {
|
||||
it('null', () => {
|
||||
const str = kbn.toDuration(null, 0, 'millisecond');
|
||||
expect(str).toBe('');
|
||||
});
|
||||
it('0 milliseconds', function() {
|
||||
it('0 milliseconds', () => {
|
||||
const str = kbn.toDuration(0, 0, 'millisecond');
|
||||
expect(str).toBe('0 milliseconds');
|
||||
});
|
||||
it('1 millisecond', function() {
|
||||
it('1 millisecond', () => {
|
||||
const str = kbn.toDuration(1, 0, 'millisecond');
|
||||
expect(str).toBe('1 millisecond');
|
||||
});
|
||||
it('-1 millisecond', function() {
|
||||
it('-1 millisecond', () => {
|
||||
const str = kbn.toDuration(-1, 0, 'millisecond');
|
||||
expect(str).toBe('1 millisecond ago');
|
||||
});
|
||||
it('seconds', function() {
|
||||
it('seconds', () => {
|
||||
const str = kbn.toDuration(1, 0, 'second');
|
||||
expect(str).toBe('1 second');
|
||||
});
|
||||
it('minutes', function() {
|
||||
it('minutes', () => {
|
||||
const str = kbn.toDuration(1, 0, 'minute');
|
||||
expect(str).toBe('1 minute');
|
||||
});
|
||||
it('hours', function() {
|
||||
it('hours', () => {
|
||||
const str = kbn.toDuration(1, 0, 'hour');
|
||||
expect(str).toBe('1 hour');
|
||||
});
|
||||
it('days', function() {
|
||||
it('days', () => {
|
||||
const str = kbn.toDuration(1, 0, 'day');
|
||||
expect(str).toBe('1 day');
|
||||
});
|
||||
it('weeks', function() {
|
||||
it('weeks', () => {
|
||||
const str = kbn.toDuration(1, 0, 'week');
|
||||
expect(str).toBe('1 week');
|
||||
});
|
||||
it('months', function() {
|
||||
it('months', () => {
|
||||
const str = kbn.toDuration(1, 0, 'month');
|
||||
expect(str).toBe('1 month');
|
||||
});
|
||||
it('years', function() {
|
||||
it('years', () => {
|
||||
const str = kbn.toDuration(1, 0, 'year');
|
||||
expect(str).toBe('1 year');
|
||||
});
|
||||
it('decimal days', function() {
|
||||
it('decimal days', () => {
|
||||
const str = kbn.toDuration(1.5, 2, 'day');
|
||||
expect(str).toBe('1 day, 12 hours, 0 minutes');
|
||||
});
|
||||
it('decimal months', function() {
|
||||
it('decimal months', () => {
|
||||
const str = kbn.toDuration(1.5, 3, 'month');
|
||||
expect(str).toBe('1 month, 2 weeks, 1 day, 0 hours');
|
||||
});
|
||||
it('no decimals', function() {
|
||||
it('no decimals', () => {
|
||||
const str = kbn.toDuration(38898367008, 0, 'millisecond');
|
||||
expect(str).toBe('1 year');
|
||||
});
|
||||
it('1 decimal', function() {
|
||||
it('1 decimal', () => {
|
||||
const str = kbn.toDuration(38898367008, 1, 'millisecond');
|
||||
expect(str).toBe('1 year, 2 months');
|
||||
});
|
||||
it('too many decimals', function() {
|
||||
it('too many decimals', () => {
|
||||
const str = kbn.toDuration(38898367008, 20, 'millisecond');
|
||||
expect(str).toBe('1 year, 2 months, 3 weeks, 4 days, 5 hours, 6 minutes, 7 seconds, 8 milliseconds');
|
||||
});
|
||||
it('floating point error', function() {
|
||||
it('floating point error', () => {
|
||||
const str = kbn.toDuration(36993906007, 8, 'millisecond');
|
||||
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() {
|
||||
describe('volume', () => {
|
||||
it('1000m3', () => {
|
||||
const str = kbn.valueFormats['m3'](1000, 1, null);
|
||||
expect(str).toBe('1000.0 m³');
|
||||
});
|
||||
});
|
||||
|
||||
describe('hh:mm:ss', function() {
|
||||
it('00:04:06', function() {
|
||||
describe('hh:mm:ss', () => {
|
||||
it('00:04:06', () => {
|
||||
const str = kbn.valueFormats['dthms'](246, 1);
|
||||
expect(str).toBe('00:04:06');
|
||||
});
|
||||
it('24:00:00', function() {
|
||||
it('24:00:00', () => {
|
||||
const str = kbn.valueFormats['dthms'](86400, 1);
|
||||
expect(str).toBe('24:00:00');
|
||||
});
|
||||
it('6824413:53:20', function() {
|
||||
it('6824413:53:20', () => {
|
||||
const str = kbn.valueFormats['dthms'](24567890000, 1);
|
||||
expect(str).toBe('6824413:53:20');
|
||||
});
|
||||
|
||||
@@ -562,5 +562,5 @@ function createCtrlWithStubs(searchResponse: any, tags?: any) {
|
||||
},
|
||||
};
|
||||
|
||||
return new ManageDashboardsCtrl({}, { getNav: () => {} }, <SearchSrv>searchSrvStub, { isEditor: true });
|
||||
return new ManageDashboardsCtrl({}, { getNav: () => {} }, searchSrvStub as SearchSrv, { isEditor: true });
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ describe('SearchCtrl', () => {
|
||||
search: (options: any) => {},
|
||||
getDashboardTags: () => {},
|
||||
};
|
||||
const ctrl = new SearchCtrl({ $on: () => {} }, {}, {}, <SearchSrv>searchSrvStub);
|
||||
const ctrl = new SearchCtrl({ $on: () => {} }, {}, {}, searchSrvStub as SearchSrv);
|
||||
|
||||
describe('Given an empty result', () => {
|
||||
beforeEach(() => {
|
||||
|
||||
@@ -26,7 +26,7 @@ describe('when sorting table desc', () => {
|
||||
});
|
||||
|
||||
describe('when sorting table asc', () => {
|
||||
var table;
|
||||
let table;
|
||||
const panel = {
|
||||
sort: { col: 1, desc: false },
|
||||
};
|
||||
@@ -46,8 +46,8 @@ describe('when sorting table asc', () => {
|
||||
});
|
||||
|
||||
describe('when sorting with nulls', () => {
|
||||
var table;
|
||||
var values;
|
||||
let table;
|
||||
let values;
|
||||
|
||||
beforeEach(() => {
|
||||
table = new TableModel();
|
||||
|
||||
@@ -1,33 +1,33 @@
|
||||
import TimeSeries from 'app/core/time_series2';
|
||||
import { updateLegendValues } from 'app/core/time_series2';
|
||||
|
||||
describe('TimeSeries', function() {
|
||||
describe('TimeSeries', () => {
|
||||
let points, series;
|
||||
const yAxisFormats = ['short', 'ms'];
|
||||
let testData;
|
||||
|
||||
beforeEach(function() {
|
||||
beforeEach(() => {
|
||||
testData = {
|
||||
alias: 'test',
|
||||
datapoints: [[1, 2], [null, 3], [10, 4], [8, 5]],
|
||||
};
|
||||
});
|
||||
|
||||
describe('when getting flot pairs', function() {
|
||||
it('with connected style, should ignore nulls', function() {
|
||||
describe('when getting flot pairs', () => {
|
||||
it('with connected style, should ignore nulls', () => {
|
||||
series = new TimeSeries(testData);
|
||||
points = series.getFlotPairs('connected', yAxisFormats);
|
||||
expect(points.length).toBe(3);
|
||||
});
|
||||
|
||||
it('with null as zero style, should replace nulls with zero', function() {
|
||||
it('with null as zero style, should replace nulls with zero', () => {
|
||||
series = new TimeSeries(testData);
|
||||
points = series.getFlotPairs('null as zero', yAxisFormats);
|
||||
expect(points.length).toBe(4);
|
||||
expect(points[1][1]).toBe(0);
|
||||
});
|
||||
|
||||
it('if last is null current should pick next to last', function() {
|
||||
it('if last is null current should pick next to last', () => {
|
||||
series = new TimeSeries({
|
||||
datapoints: [[10, 1], [null, 2]],
|
||||
});
|
||||
@@ -35,7 +35,7 @@ describe('TimeSeries', function() {
|
||||
expect(series.stats.current).toBe(10);
|
||||
});
|
||||
|
||||
it('max value should work for negative values', function() {
|
||||
it('max value should work for negative values', () => {
|
||||
series = new TimeSeries({
|
||||
datapoints: [[-10, 1], [-4, 2]],
|
||||
});
|
||||
@@ -43,13 +43,13 @@ describe('TimeSeries', function() {
|
||||
expect(series.stats.max).toBe(-4);
|
||||
});
|
||||
|
||||
it('average value should ignore nulls', function() {
|
||||
it('average value should ignore nulls', () => {
|
||||
series = new TimeSeries(testData);
|
||||
series.getFlotPairs('null', yAxisFormats);
|
||||
expect(series.stats.avg).toBe(6.333333333333333);
|
||||
});
|
||||
|
||||
it('the delta value should account for nulls', function() {
|
||||
it('the delta value should account for nulls', () => {
|
||||
series = new TimeSeries({
|
||||
datapoints: [[1, 2], [3, 3], [null, 4], [10, 5], [15, 6]],
|
||||
});
|
||||
@@ -57,7 +57,7 @@ describe('TimeSeries', function() {
|
||||
expect(series.stats.delta).toBe(14);
|
||||
});
|
||||
|
||||
it('the delta value should account for nulls on first', function() {
|
||||
it('the delta value should account for nulls on first', () => {
|
||||
series = new TimeSeries({
|
||||
datapoints: [[null, 2], [1, 3], [10, 4], [15, 5]],
|
||||
});
|
||||
@@ -65,7 +65,7 @@ describe('TimeSeries', function() {
|
||||
expect(series.stats.delta).toBe(14);
|
||||
});
|
||||
|
||||
it('the delta value should account for nulls on last', function() {
|
||||
it('the delta value should account for nulls on last', () => {
|
||||
series = new TimeSeries({
|
||||
datapoints: [[1, 2], [5, 3], [10, 4], [null, 5]],
|
||||
});
|
||||
@@ -73,7 +73,7 @@ describe('TimeSeries', function() {
|
||||
expect(series.stats.delta).toBe(9);
|
||||
});
|
||||
|
||||
it('the delta value should account for resets', function() {
|
||||
it('the delta value should account for resets', () => {
|
||||
series = new TimeSeries({
|
||||
datapoints: [[1, 2], [5, 3], [10, 4], [0, 5], [10, 6]],
|
||||
});
|
||||
@@ -81,7 +81,7 @@ describe('TimeSeries', function() {
|
||||
expect(series.stats.delta).toBe(19);
|
||||
});
|
||||
|
||||
it('the delta value should account for resets on last', function() {
|
||||
it('the delta value should account for resets on last', () => {
|
||||
series = new TimeSeries({
|
||||
datapoints: [[1, 2], [2, 3], [10, 4], [8, 5]],
|
||||
});
|
||||
@@ -89,13 +89,13 @@ describe('TimeSeries', function() {
|
||||
expect(series.stats.delta).toBe(17);
|
||||
});
|
||||
|
||||
it('the range value should be max - min', function() {
|
||||
it('the range value should be max - min', () => {
|
||||
series = new TimeSeries(testData);
|
||||
series.getFlotPairs('null', yAxisFormats);
|
||||
expect(series.stats.range).toBe(9);
|
||||
});
|
||||
|
||||
it('first value should ingone nulls', function() {
|
||||
it('first value should ingone nulls', () => {
|
||||
series = new TimeSeries(testData);
|
||||
series.getFlotPairs('null', yAxisFormats);
|
||||
expect(series.stats.first).toBe(1);
|
||||
@@ -106,13 +106,13 @@ describe('TimeSeries', function() {
|
||||
expect(series.stats.first).toBe(1);
|
||||
});
|
||||
|
||||
it('with null as zero style, average value should treat nulls as 0', function() {
|
||||
it('with null as zero style, average value should treat nulls as 0', () => {
|
||||
series = new TimeSeries(testData);
|
||||
series.getFlotPairs('null as zero', yAxisFormats);
|
||||
expect(series.stats.avg).toBe(4.75);
|
||||
});
|
||||
|
||||
it('average value should be null if all values is null', function() {
|
||||
it('average value should be null if all values is null', () => {
|
||||
series = new TimeSeries({
|
||||
datapoints: [[null, 2], [null, 3], [null, 4], [null, 5]],
|
||||
});
|
||||
@@ -120,7 +120,7 @@ describe('TimeSeries', function() {
|
||||
expect(series.stats.avg).toBe(null);
|
||||
});
|
||||
|
||||
it('calculates timeStep', function() {
|
||||
it('calculates timeStep', () => {
|
||||
series = new TimeSeries({
|
||||
datapoints: [[null, 1], [null, 2], [null, 3]],
|
||||
});
|
||||
@@ -135,190 +135,190 @@ describe('TimeSeries', function() {
|
||||
});
|
||||
});
|
||||
|
||||
describe('When checking if ms resolution is needed', function() {
|
||||
describe('msResolution with second resolution timestamps', function() {
|
||||
beforeEach(function() {
|
||||
describe('When checking if ms resolution is needed', () => {
|
||||
describe('msResolution with second resolution timestamps', () => {
|
||||
beforeEach(() => {
|
||||
series = new TimeSeries({
|
||||
datapoints: [[45, 1234567890], [60, 1234567899]],
|
||||
});
|
||||
});
|
||||
|
||||
it('should set hasMsResolution to false', function() {
|
||||
it('should set hasMsResolution to false', () => {
|
||||
expect(series.hasMsResolution).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('msResolution with millisecond resolution timestamps', function() {
|
||||
beforeEach(function() {
|
||||
describe('msResolution with millisecond resolution timestamps', () => {
|
||||
beforeEach(() => {
|
||||
series = new TimeSeries({
|
||||
datapoints: [[55, 1236547890001], [90, 1234456709000]],
|
||||
});
|
||||
});
|
||||
|
||||
it('should show millisecond resolution tooltip', function() {
|
||||
it('should show millisecond resolution tooltip', () => {
|
||||
expect(series.hasMsResolution).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('msResolution with millisecond resolution timestamps but with trailing zeroes', function() {
|
||||
beforeEach(function() {
|
||||
describe('msResolution with millisecond resolution timestamps but with trailing zeroes', () => {
|
||||
beforeEach(() => {
|
||||
series = new TimeSeries({
|
||||
datapoints: [[45, 1234567890000], [60, 1234567899000]],
|
||||
});
|
||||
});
|
||||
|
||||
it('should not show millisecond resolution tooltip', function() {
|
||||
it('should not show millisecond resolution tooltip', () => {
|
||||
expect(series.hasMsResolution).toBe(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('can detect if series contains ms precision', function() {
|
||||
var fakedata;
|
||||
describe('can detect if series contains ms precision', () => {
|
||||
let fakedata;
|
||||
|
||||
beforeEach(function() {
|
||||
beforeEach(() => {
|
||||
fakedata = testData;
|
||||
});
|
||||
|
||||
it('missing datapoint with ms precision', function() {
|
||||
it('missing datapoint with ms precision', () => {
|
||||
fakedata.datapoints[0] = [1337, 1234567890000];
|
||||
series = new TimeSeries(fakedata);
|
||||
expect(series.isMsResolutionNeeded()).toBe(false);
|
||||
});
|
||||
|
||||
it('contains datapoint with ms precision', function() {
|
||||
it('contains datapoint with ms precision', () => {
|
||||
fakedata.datapoints[0] = [1337, 1236547890001];
|
||||
series = new TimeSeries(fakedata);
|
||||
expect(series.isMsResolutionNeeded()).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('series overrides', function() {
|
||||
var series;
|
||||
beforeEach(function() {
|
||||
describe('series overrides', () => {
|
||||
let series;
|
||||
beforeEach(() => {
|
||||
series = new TimeSeries(testData);
|
||||
});
|
||||
|
||||
describe('fill & points', function() {
|
||||
beforeEach(function() {
|
||||
describe('fill & points', () => {
|
||||
beforeEach(() => {
|
||||
series.alias = 'test';
|
||||
series.applySeriesOverrides([{ alias: 'test', fill: 0, points: true }]);
|
||||
});
|
||||
|
||||
it('should set fill zero, and enable points', function() {
|
||||
it('should set fill zero, and enable points', () => {
|
||||
expect(series.lines.fill).toBe(0.001);
|
||||
expect(series.points.show).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('series option overrides, bars, true & lines false', function() {
|
||||
beforeEach(function() {
|
||||
describe('series option overrides, bars, true & lines false', () => {
|
||||
beforeEach(() => {
|
||||
series.alias = 'test';
|
||||
series.applySeriesOverrides([{ alias: 'test', bars: true, lines: false }]);
|
||||
});
|
||||
|
||||
it('should disable lines, and enable bars', function() {
|
||||
it('should disable lines, and enable bars', () => {
|
||||
expect(series.lines.show).toBe(false);
|
||||
expect(series.bars.show).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('series option overrides, linewidth, stack', function() {
|
||||
beforeEach(function() {
|
||||
describe('series option overrides, linewidth, stack', () => {
|
||||
beforeEach(() => {
|
||||
series.alias = 'test';
|
||||
series.applySeriesOverrides([{ alias: 'test', linewidth: 5, stack: false }]);
|
||||
});
|
||||
|
||||
it('should disable stack, and set lineWidth', function() {
|
||||
it('should disable stack, and set lineWidth', () => {
|
||||
expect(series.stack).toBe(false);
|
||||
expect(series.lines.lineWidth).toBe(5);
|
||||
});
|
||||
});
|
||||
|
||||
describe('series option overrides, dashes and lineWidth', function() {
|
||||
beforeEach(function() {
|
||||
describe('series option overrides, dashes and lineWidth', () => {
|
||||
beforeEach(() => {
|
||||
series.alias = 'test';
|
||||
series.applySeriesOverrides([{ alias: 'test', linewidth: 5, dashes: true }]);
|
||||
});
|
||||
|
||||
it('should enable dashes, set dashes lineWidth to 5 and lines lineWidth to 0', function() {
|
||||
it('should enable dashes, set dashes lineWidth to 5 and lines lineWidth to 0', () => {
|
||||
expect(series.dashes.show).toBe(true);
|
||||
expect(series.dashes.lineWidth).toBe(5);
|
||||
expect(series.lines.lineWidth).toBe(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe('series option overrides, fill below to', function() {
|
||||
beforeEach(function() {
|
||||
describe('series option overrides, fill below to', () => {
|
||||
beforeEach(() => {
|
||||
series.alias = 'test';
|
||||
series.applySeriesOverrides([{ alias: 'test', fillBelowTo: 'min' }]);
|
||||
});
|
||||
|
||||
it('should disable line fill and add fillBelowTo', function() {
|
||||
it('should disable line fill and add fillBelowTo', () => {
|
||||
expect(series.fillBelowTo).toBe('min');
|
||||
});
|
||||
});
|
||||
|
||||
describe('series option overrides, pointradius, steppedLine', function() {
|
||||
beforeEach(function() {
|
||||
describe('series option overrides, pointradius, steppedLine', () => {
|
||||
beforeEach(() => {
|
||||
series.alias = 'test';
|
||||
series.applySeriesOverrides([{ alias: 'test', pointradius: 5, steppedLine: true }]);
|
||||
});
|
||||
|
||||
it('should set pointradius, and set steppedLine', function() {
|
||||
it('should set pointradius, and set steppedLine', () => {
|
||||
expect(series.points.radius).toBe(5);
|
||||
expect(series.lines.steps).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('override match on regex', function() {
|
||||
beforeEach(function() {
|
||||
describe('override match on regex', () => {
|
||||
beforeEach(() => {
|
||||
series.alias = 'test_01';
|
||||
series.applySeriesOverrides([{ alias: '/.*01/', lines: false }]);
|
||||
});
|
||||
|
||||
it('should match second series', function() {
|
||||
it('should match second series', () => {
|
||||
expect(series.lines.show).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('override series y-axis, and z-index', function() {
|
||||
beforeEach(function() {
|
||||
describe('override series y-axis, and z-index', () => {
|
||||
beforeEach(() => {
|
||||
series.alias = 'test';
|
||||
series.applySeriesOverrides([{ alias: 'test', yaxis: 2, zindex: 2 }]);
|
||||
});
|
||||
|
||||
it('should set yaxis', function() {
|
||||
it('should set yaxis', () => {
|
||||
expect(series.yaxis).toBe(2);
|
||||
});
|
||||
|
||||
it('should set zindex', function() {
|
||||
it('should set zindex', () => {
|
||||
expect(series.zindex).toBe(2);
|
||||
});
|
||||
});
|
||||
|
||||
describe('override color', function() {
|
||||
beforeEach(function() {
|
||||
describe('override color', () => {
|
||||
beforeEach(() => {
|
||||
series.applySeriesOverrides([{ alias: 'test', color: '#112233' }]);
|
||||
});
|
||||
|
||||
it('should set color', function() {
|
||||
it('should set color', () => {
|
||||
expect(series.color).toBe('#112233');
|
||||
});
|
||||
|
||||
it('should set bars.fillColor', function() {
|
||||
it('should set bars.fillColor', () => {
|
||||
expect(series.bars.fillColor).toBe('#112233');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('value formatter', function() {
|
||||
var series;
|
||||
beforeEach(function() {
|
||||
describe('value formatter', () => {
|
||||
let series;
|
||||
beforeEach(() => {
|
||||
series = new TimeSeries(testData);
|
||||
});
|
||||
|
||||
it('should format non-numeric values as empty string', function() {
|
||||
it('should format non-numeric values as empty string', () => {
|
||||
expect(series.formatValue(null)).toBe('');
|
||||
expect(series.formatValue(undefined)).toBe('');
|
||||
expect(series.formatValue(NaN)).toBe('');
|
||||
@@ -327,10 +327,10 @@ describe('TimeSeries', function() {
|
||||
});
|
||||
});
|
||||
|
||||
describe('legend decimals', function() {
|
||||
describe('legend decimals', () => {
|
||||
let series, panel;
|
||||
const height = 200;
|
||||
beforeEach(function() {
|
||||
beforeEach(() => {
|
||||
testData = {
|
||||
alias: 'test',
|
||||
datapoints: [[1, 2], [0, 3], [10, 4], [8, 5]],
|
||||
@@ -347,14 +347,14 @@ describe('TimeSeries', function() {
|
||||
};
|
||||
});
|
||||
|
||||
it('should set decimals based on Y axis (expect calculated decimals = 1)', function() {
|
||||
it('should set decimals based on Y axis (expect calculated decimals = 1)', () => {
|
||||
const data = [series];
|
||||
// Expect ticks with this data will have decimals = 1
|
||||
updateLegendValues(data, panel, height);
|
||||
expect(data[0].decimals).toBe(2);
|
||||
});
|
||||
|
||||
it('should set decimals based on Y axis to 0 if calculated decimals = 0)', function() {
|
||||
it('should set decimals based on Y axis to 0 if calculated decimals = 0)', () => {
|
||||
testData.datapoints = [[10, 2], [0, 3], [100, 4], [80, 5]];
|
||||
series = new TimeSeries(testData);
|
||||
series.getFlotPairs();
|
||||
@@ -363,14 +363,14 @@ describe('TimeSeries', function() {
|
||||
expect(data[0].decimals).toBe(0);
|
||||
});
|
||||
|
||||
it('should set decimals to Y axis decimals + 1', function() {
|
||||
it('should set decimals to Y axis decimals + 1', () => {
|
||||
panel.yaxes[0].decimals = 2;
|
||||
const data = [series];
|
||||
updateLegendValues(data, panel, height);
|
||||
expect(data[0].decimals).toBe(3);
|
||||
});
|
||||
|
||||
it('should set decimals to legend decimals value if it was set explicitly', function() {
|
||||
it('should set decimals to legend decimals value if it was set explicitly', () => {
|
||||
panel.decimals = 3;
|
||||
const data = [series];
|
||||
updateLegendValues(data, panel, height);
|
||||
|
||||
@@ -26,7 +26,7 @@ export default class TableModel {
|
||||
return;
|
||||
}
|
||||
|
||||
this.rows.sort(function(a, b) {
|
||||
this.rows.sort((a, b) => {
|
||||
a = a[options.col];
|
||||
b = b[options.col];
|
||||
// Sort null or undefined seperately from comparable values
|
||||
|
||||
@@ -8,7 +8,7 @@ function matchSeriesOverride(aliasOrRegex, seriesAlias) {
|
||||
}
|
||||
|
||||
if (aliasOrRegex[0] === '/') {
|
||||
var regex = kbn.stringToJsRegex(aliasOrRegex);
|
||||
const regex = kbn.stringToJsRegex(aliasOrRegex);
|
||||
return seriesAlias.match(regex) != null;
|
||||
}
|
||||
|
||||
@@ -43,9 +43,9 @@ export function updateLegendValues(data: TimeSeries[], panel, height) {
|
||||
// legend and tooltip gets one more decimal precision
|
||||
// than graph legend ticks
|
||||
const { datamin, datamax } = getDataMinMax(data);
|
||||
let { tickDecimals, scaledDecimals } = getFlotTickDecimals(datamin, datamax, axis, height);
|
||||
tickDecimals = (tickDecimals || -1) + 1;
|
||||
series.updateLegendValues(formater, tickDecimals, scaledDecimals + 2);
|
||||
const { tickDecimals, scaledDecimals } = getFlotTickDecimals(datamin, datamax, axis, height);
|
||||
const tickDecimalsPlusOne = (tickDecimals || -1) + 1;
|
||||
series.updateLegendValues(formater, tickDecimalsPlusOne, scaledDecimals + 2);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -124,8 +124,8 @@ export default class TimeSeries {
|
||||
delete this.stack;
|
||||
delete this.bars.show;
|
||||
|
||||
for (var i = 0; i < overrides.length; i++) {
|
||||
var override = overrides[i];
|
||||
for (let i = 0; i < overrides.length; i++) {
|
||||
const override = overrides[i];
|
||||
if (!matchSeriesOverride(override.alias, this.alias)) {
|
||||
continue;
|
||||
}
|
||||
@@ -193,7 +193,7 @@ export default class TimeSeries {
|
||||
}
|
||||
|
||||
getFlotPairs(fillStyle) {
|
||||
var result = [];
|
||||
const result = [];
|
||||
|
||||
this.stats.total = 0;
|
||||
this.stats.max = -Number.MAX_VALUE;
|
||||
@@ -209,16 +209,16 @@ export default class TimeSeries {
|
||||
this.allIsNull = true;
|
||||
this.allIsZero = true;
|
||||
|
||||
var ignoreNulls = fillStyle === 'connected';
|
||||
var nullAsZero = fillStyle === 'null as zero';
|
||||
var currentTime;
|
||||
var currentValue;
|
||||
var nonNulls = 0;
|
||||
var previousTime;
|
||||
var previousValue = 0;
|
||||
var previousDeltaUp = true;
|
||||
const ignoreNulls = fillStyle === 'connected';
|
||||
const nullAsZero = fillStyle === 'null as zero';
|
||||
let currentTime;
|
||||
let currentValue;
|
||||
let nonNulls = 0;
|
||||
let previousTime;
|
||||
let previousValue = 0;
|
||||
let previousDeltaUp = true;
|
||||
|
||||
for (var i = 0; i < this.datapoints.length; i++) {
|
||||
for (let i = 0; i < this.datapoints.length; i++) {
|
||||
currentValue = this.datapoints[i][0];
|
||||
currentTime = this.datapoints[i][1];
|
||||
|
||||
@@ -328,9 +328,9 @@ export default class TimeSeries {
|
||||
}
|
||||
|
||||
isMsResolutionNeeded() {
|
||||
for (var i = 0; i < this.datapoints.length; i++) {
|
||||
for (let i = 0; i < this.datapoints.length; i++) {
|
||||
if (this.datapoints[i][1] !== null) {
|
||||
var timestamp = this.datapoints[i][1].toString();
|
||||
const timestamp = this.datapoints[i][1].toString();
|
||||
if (timestamp.length === 13 && timestamp % 1000 !== 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user