New to Grafana?
diff --git a/public/app/plugins/datasource/graphite/query_ctrl.ts b/public/app/plugins/datasource/graphite/query_ctrl.ts
index 29ff29b7ae3..0563de61705 100644
--- a/public/app/plugins/datasource/graphite/query_ctrl.ts
+++ b/public/app/plugins/datasource/graphite/query_ctrl.ts
@@ -348,6 +348,10 @@ export class GraphiteQueryCtrl extends QueryCtrl {
let tagKey = tag.key;
return this.datasource.getTagValuesAutoComplete(tagExpressions, tagKey, valuePrefix).then(values => {
let altValues = _.map(values, 'text');
+ // Add template variables as additional values
+ _.eachRight(this.templateSrv.variables, variable => {
+ altValues.push('${' + variable.name + ':regex}');
+ });
return mapToDropdownOptions(altValues);
});
}
diff --git a/public/app/plugins/datasource/prometheus/img/prometheus_logo.svg b/public/app/plugins/datasource/prometheus/img/prometheus_logo.svg
index 29005ec3860..4c4448862e6 100644
--- a/public/app/plugins/datasource/prometheus/img/prometheus_logo.svg
+++ b/public/app/plugins/datasource/prometheus/img/prometheus_logo.svg
@@ -1,19 +1 @@
-
-
-
-
+
\ No newline at end of file
diff --git a/public/app/stores/PermissionsStore/PermissionsStore.jest.ts b/public/app/stores/PermissionsStore/PermissionsStore.jest.ts
index f7332516bef..c3bc6016e50 100644
--- a/public/app/stores/PermissionsStore/PermissionsStore.jest.ts
+++ b/public/app/stores/PermissionsStore/PermissionsStore.jest.ts
@@ -1,10 +1,10 @@
-import { PermissionsStore, aclTypeValues } from './PermissionsStore';
+import { PermissionsStore } from './PermissionsStore';
import { backendSrv } from 'test/mocks/common';
describe('PermissionsStore', () => {
let store;
- beforeEach(() => {
+ beforeEach(async () => {
backendSrv.get.mockReturnValue(
Promise.resolve([
{ id: 2, dashboardId: 1, role: 'Viewer', permission: 1, permissionName: 'View' },
@@ -20,7 +20,7 @@ describe('PermissionsStore', () => {
])
);
- backendSrv.post = jest.fn();
+ backendSrv.post = jest.fn(() => Promise.resolve({}));
store = PermissionsStore.create(
{
@@ -32,14 +32,14 @@ describe('PermissionsStore', () => {
}
);
- return store.load(1, false, false);
+ await store.load(1, false, false);
});
- it('should save update on permission change', () => {
+ it('should save update on permission change', async () => {
expect(store.items[0].permission).toBe(1);
expect(store.items[0].permissionName).toBe('View');
- store.updatePermissionOnIndex(0, 2, 'Edit');
+ await store.updatePermissionOnIndex(0, 2, 'Edit');
expect(store.items[0].permission).toBe(2);
expect(store.items[0].permissionName).toBe('Edit');
@@ -47,69 +47,18 @@ describe('PermissionsStore', () => {
expect(backendSrv.post.mock.calls[0][0]).toBe('/api/dashboards/id/1/permissions');
});
- it('should save removed permissions automatically', () => {
+ it('should save removed permissions automatically', async () => {
expect(store.items.length).toBe(3);
- store.removeStoreItem(2);
+ await store.removeStoreItem(2);
expect(store.items.length).toBe(2);
expect(backendSrv.post.mock.calls.length).toBe(1);
expect(backendSrv.post.mock.calls[0][0]).toBe('/api/dashboards/id/1/permissions');
});
- describe('when duplicate team permissions are added', () => {
- beforeEach(() => {
- const newItem = {
- teamId: 10,
- team: 'tester-team',
- permission: 1,
- dashboardId: 1,
- };
- store.resetNewType();
- store.newItem.setTeam(newItem.teamId, newItem.team);
- store.newItem.setPermission(newItem.permission);
- store.addStoreItem();
-
- store.newItem.setTeam(newItem.teamId, newItem.team);
- store.newItem.setPermission(newItem.permission);
- store.addStoreItem();
- });
-
- it('should return a validation error', () => {
- expect(store.items.length).toBe(4);
- expect(store.error).toBe('This permission exists already.');
- expect(backendSrv.post.mock.calls.length).toBe(1);
- });
- });
-
- describe('when duplicate user permissions are added', () => {
- beforeEach(() => {
- expect(store.items.length).toBe(3);
- const newItem = {
- userId: 10,
- userLogin: 'tester1',
- permission: 1,
- dashboardId: 1,
- };
- store.setNewType(aclTypeValues.USER.value);
- store.newItem.setUser(newItem.userId, newItem.userLogin);
- store.newItem.setPermission(newItem.permission);
- store.addStoreItem();
- store.setNewType(aclTypeValues.USER.value);
- store.newItem.setUser(newItem.userId, newItem.userLogin);
- store.newItem.setPermission(newItem.permission);
- store.addStoreItem();
- });
-
- it('should return a validation error', () => {
- expect(store.items.length).toBe(4);
- expect(store.error).toBe('This permission exists already.');
- expect(backendSrv.post.mock.calls.length).toBe(1);
- });
- });
-
describe('when one inherited and one not inherited team permission are added', () => {
- beforeEach(() => {
+ beforeEach(async () => {
const overridingItemForChildDashboard = {
team: 'MyTestTeam',
dashboardId: 1,
@@ -120,11 +69,7 @@ describe('PermissionsStore', () => {
store.resetNewType();
store.newItem.setTeam(overridingItemForChildDashboard.teamId, overridingItemForChildDashboard.team);
store.newItem.setPermission(overridingItemForChildDashboard.permission);
- store.addStoreItem();
- });
-
- it('should allowing overriding the inherited permission and not throw a validation error', () => {
- expect(store.error).toBe(null);
+ await store.addStoreItem();
});
it('should add new overriding permission', () => {
diff --git a/public/app/stores/PermissionsStore/PermissionsStore.ts b/public/app/stores/PermissionsStore/PermissionsStore.ts
index 88b26a2886f..79df593f06e 100644
--- a/public/app/stores/PermissionsStore/PermissionsStore.ts
+++ b/public/app/stores/PermissionsStore/PermissionsStore.ts
@@ -1,8 +1,6 @@
import { types, getEnv, flow } from 'mobx-state-tree';
import { PermissionsStoreItem } from './PermissionsStoreItem';
-const duplicateError = 'This permission exists already.';
-
export const permissionOptions = [
{ value: 1, label: 'View', description: 'Can view dashboards.' },
{ value: 2, label: 'Edit', description: 'Can add, edit and delete dashboards.' },
@@ -75,7 +73,6 @@ export const PermissionsStore = types
isFolder: types.maybe(types.boolean),
dashboardId: types.maybe(types.number),
items: types.optional(types.array(PermissionsStoreItem), []),
- error: types.maybe(types.string),
originalItems: types.optional(types.array(PermissionsStoreItem), []),
newType: types.optional(types.string, defaultNewType),
newItem: types.maybe(NewPermissionsItem),
@@ -88,7 +85,6 @@ export const PermissionsStore = types
return isDuplicate(it, item);
});
if (dupe) {
- self.error = duplicateError;
return false;
}
@@ -96,8 +92,7 @@ export const PermissionsStore = types
},
}))
.actions(self => {
- const resetNewType = () => {
- self.error = null;
+ const resetNewTypeInternal = () => {
self.newItem = NewPermissionsItem.create();
};
@@ -115,11 +110,9 @@ export const PermissionsStore = types
self.items = items;
self.originalItems = items;
self.fetching = false;
- self.error = null;
}),
addStoreItem: flow(function* addStoreItem() {
- self.error = null;
let item = {
type: self.newItem.type,
permission: self.newItem.permission,
@@ -147,19 +140,21 @@ export const PermissionsStore = types
throw Error('Unknown type: ' + self.newItem.type);
}
- if (!self.isValid(item)) {
- return undefined;
- }
+ const updatedItems = self.items.peek();
+ const newItem = prepareItem(item, self.dashboardId, self.isFolder, self.isInRoot);
+ updatedItems.push(newItem);
- self.items.push(prepareItem(item, self.dashboardId, self.isFolder, self.isInRoot));
- resetNewType();
- return updateItems(self);
+ try {
+ yield updateItems(self, updatedItems);
+ self.items.push(newItem);
+ resetNewTypeInternal();
+ } catch {}
+ yield Promise.resolve();
}),
removeStoreItem: flow(function* removeStoreItem(idx: number) {
- self.error = null;
self.items.splice(idx, 1);
- return updateItems(self);
+ yield updateItems(self, self.items.peek());
}),
updatePermissionOnIndex: flow(function* updatePermissionOnIndex(
@@ -167,9 +162,8 @@ export const PermissionsStore = types
permission: number,
permissionName: string
) {
- self.error = null;
self.items[idx].updatePermission(permission, permissionName);
- return updateItems(self);
+ yield updateItems(self, self.items.peek());
}),
setNewType(newType: string) {
@@ -177,7 +171,7 @@ export const PermissionsStore = types
},
resetNewType() {
- resetNewType();
+ resetNewTypeInternal();
},
toggleAddPermissions() {
@@ -190,12 +184,10 @@ export const PermissionsStore = types
};
});
-const updateItems = self => {
- self.error = null;
-
+const updateItems = (self, items) => {
const backendSrv = getEnv(self).backendSrv;
const updated = [];
- for (let item of self.items) {
+ for (let item of items) {
if (item.inherited) {
continue;
}
@@ -208,16 +200,9 @@ const updateItems = self => {
});
}
- let res;
- try {
- res = backendSrv.post(`/api/dashboards/id/${self.dashboardId}/permissions`, {
- items: updated,
- });
- } catch (error) {
- self.error = error;
- }
-
- return res;
+ return backendSrv.post(`/api/dashboards/id/${self.dashboardId}/permissions`, {
+ items: updated,
+ });
};
const prepareServerResponse = (response, dashboardId: number, isFolder: boolean, isInRoot: boolean) => {
diff --git a/public/sass/components/_dashboard_grid.scss b/public/sass/components/_dashboard_grid.scss
index 0a27df75164..dd81a210409 100644
--- a/public/sass/components/_dashboard_grid.scss
+++ b/public/sass/components/_dashboard_grid.scss
@@ -1,4 +1,4 @@
-@import '~react-grid-layout/css/styles.css';
+@import '~react-grid-layout-grafana/css/styles.css';
@import '~react-resizable/css/styles.css';
.panel-in-fullscreen {
diff --git a/public/sass/components/_view_states.scss b/public/sass/components/_view_states.scss
index be5361ceeb4..b1fa47d0c0a 100644
--- a/public/sass/components/_view_states.scss
+++ b/public/sass/components/_view_states.scss
@@ -3,6 +3,9 @@
.navbar {
display: none;
}
+ .scroll-canvas--dashboard {
+ height: 100%;
+ }
}
.playlist-active,
diff --git a/yarn.lock b/yarn.lock
index 692eb83ea96..2de60f03c27 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -8275,16 +8275,23 @@ react-dom@^16.2.0:
object-assign "^4.1.1"
prop-types "^15.6.0"
-"react-draggable@^2.2.6 || ^3.0.3", react-draggable@^3.0.3:
+"react-draggable@^2.2.6 || ^3.0.3":
version "3.0.3"
resolved "https://registry.yarnpkg.com/react-draggable/-/react-draggable-3.0.3.tgz#a6f9b3a7171981b76dadecf238316925cb9eacf4"
dependencies:
classnames "^2.2.5"
prop-types "^15.5.10"
-react-grid-layout@^0.16.2:
- version "0.16.2"
- resolved "https://registry.yarnpkg.com/react-grid-layout/-/react-grid-layout-0.16.2.tgz#ef09b0b6db4a9635799663658277ee2d26fa2994"
+react-draggable@^3.0.3:
+ version "3.0.5"
+ resolved "https://registry.yarnpkg.com/react-draggable/-/react-draggable-3.0.5.tgz#c031e0ed4313531f9409d6cd84c8ebcec0ddfe2d"
+ dependencies:
+ classnames "^2.2.5"
+ prop-types "^15.6.0"
+
+react-grid-layout-grafana@0.16.0:
+ version "0.16.0"
+ resolved "https://registry.yarnpkg.com/react-grid-layout-grafana/-/react-grid-layout-grafana-0.16.0.tgz#12242153fcd0bb80a26af8e41694bc2fde788b3a"
dependencies:
classnames "2.x"
lodash.isequal "^4.0.0"