Merge pull request #11142 from daniellee/v5.0.x

Cherrypick for V5.0.x
pull/11148/head
Daniel Lee 7 years ago committed by GitHub
commit 5ad76a29a0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 1
      public/app/core/components/scroll/scroll.ts
  2. 125
      public/app/core/components/sidemenu/sidemenu.html
  3. 19
      public/app/features/dashboard/dashgrid/DashboardRow.tsx
  4. 23
      public/app/features/dashboard/specs/DashboardRow.jest.tsx
  5. 1
      public/sass/_variables.dark.scss
  6. 1
      public/sass/_variables.light.scss
  7. 11
      public/sass/components/_sidemenu.scss

@ -8,6 +8,7 @@ export function geminiScrollbar() {
link: function(scope, elem, attrs) {
let scrollbar = new PerfectScrollbar(elem[0], {
wheelPropagation: true,
wheelSpeed: 3,
});
let lastPos = 0;

@ -1,73 +1,78 @@
<a class="sidemenu__logo" ng-click="ctrl.toggleSideMenu()">
<img src="public/img/grafana_icon.svg"></img>
<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>&nbsp;Close</span>
<span class="sidemenu__close">
<i class="fa fa-times"></i>&nbsp;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 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>
<ul class="dropdown-menu dropdown-menu--sidemenu" role="menu">
<li class="side-menu-header">
<span class="sidemenu-item-text">Sign In</span>
</li>
</ul>
</div>
<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}}">
<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.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 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.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>

@ -4,6 +4,7 @@ import { PanelModel } from '../panel_model';
import { PanelContainer } from './PanelContainer';
import templateSrv from 'app/features/templating/template_srv';
import appEvents from 'app/core/app_events';
import config from 'app/core/config';
export interface DashboardRowProps {
panel: PanelModel;
@ -94,14 +95,16 @@ export class DashboardRow extends React.Component<DashboardRowProps, any> {
{title}
<span className="dashboard-row__panel_count">({hiddenPanels} hidden panels)</span>
</a>
<div className="dashboard-row__actions">
<a className="pointer" onClick={this.openSettings}>
<i className="fa fa-cog" />
</a>
<a className="pointer" onClick={this.delete}>
<i className="fa fa-trash" />
</a>
</div>
{config.bootData.user.orgRole !== 'Viewer' && (
<div className="dashboard-row__actions">
<a className="pointer" onClick={this.openSettings}>
<i className="fa fa-cog" />
</a>
<a className="pointer" onClick={this.delete}>
<i className="fa fa-trash" />
</a>
</div>
)}
<div className="dashboard-row__drag grid-drag-handle" />
</div>
);

@ -2,19 +2,26 @@ import React from 'react';
import { shallow } from 'enzyme';
import { DashboardRow } from '../dashgrid/DashboardRow';
import { PanelModel } from '../panel_model';
import config from '../../../core/config';
describe('DashboardRow', () => {
let wrapper, panel, getPanelContainer, dashboardMock;
beforeEach(() => {
dashboardMock = {toggleRow: jest.fn()};
dashboardMock = { toggleRow: jest.fn() };
config.bootData = {
user: {
orgRole: 'Admin',
},
};
getPanelContainer = jest.fn().mockReturnValue({
getDashboard: jest.fn().mockReturnValue(dashboardMock),
getPanelLoader: jest.fn()
getPanelLoader: jest.fn(),
});
panel = new PanelModel({collapsed: false});
panel = new PanelModel({ collapsed: false });
wrapper = shallow(<DashboardRow panel={panel} getPanelContainer={getPanelContainer} />);
});
@ -30,4 +37,14 @@ describe('DashboardRow', () => {
expect(dashboardMock.toggleRow.mock.calls).toHaveLength(1);
});
it('should have two actions as admin', () => {
expect(wrapper.find('.dashboard-row__actions .pointer')).toHaveLength(2);
});
it('should have zero actions as viewer', () => {
config.bootData.user.orgRole = 'Viewer';
panel = new PanelModel({ collapsed: false });
wrapper = shallow(<DashboardRow panel={panel} getPanelContainer={getPanelContainer} />);
expect(wrapper.find('.dashboard-row__actions .pointer')).toHaveLength(0);
});
});

@ -259,6 +259,7 @@ $navbar-button-border: #2f2f32;
// Sidemenu
// -------------------------
$side-menu-bg: $black;
$side-menu-bg-mobile: $side-menu-bg;
$side-menu-item-hover-bg: $dark-2;
$side-menu-shadow: 0 0 20px black;
$side-menu-link-color: $link-color;

@ -200,6 +200,7 @@ $input-invalid-border-color: lighten($red, 5%);
// Sidemenu
// -------------------------
$side-menu-bg: $dark-2;
$side-menu-bg-mobile: rgba(0, 0, 0, 0); //$gray-6;
$side-menu-item-hover-bg: $gray-1;
$side-menu-shadow: 5px 0px 10px -5px $gray-1;
$side-menu-link-color: $gray-6;

@ -71,7 +71,7 @@
// important to overlap it otherwise it can be hidden
// again by the mouse getting outside the hover space
left: $side-menu-width - 2px;
@include animation("dropdown-anim 150ms ease-in-out 100ms forwards");
@include animation('dropdown-anim 150ms ease-in-out 100ms forwards');
z-index: $zindex-sidemenu;
}
}
@ -193,9 +193,13 @@ li.sidemenu-org-switcher {
@include media-breakpoint-down(xs) {
.sidemenu-open--xs {
li {
font-size: $font-size-lg;
}
.sidemenu {
width: 100%;
background: $side-menu-bg;
background: $side-menu-bg-mobile;
position: initial;
height: auto;
box-shadow: $side-menu-shadow;
@ -214,6 +218,9 @@ li.sidemenu-org-switcher {
.sidemenu__bottom {
display: block;
}
.sidemenu-item {
border-right: 2px solid transparent;
}
}
.sidemenu {

Loading…
Cancel
Save