Commit 5ee2d1de by Daniel Lee

dashfolders: select with description for permissions

The dropdown for selecting permission is a new component built on
react-select that includes a description for the permission for
every option in the select.
parent a7fba593
...@@ -83,18 +83,18 @@ func (g *DashboardGuardian) checkAcl(permission m.PermissionType, acl []*m.Dashb ...@@ -83,18 +83,18 @@ func (g *DashboardGuardian) checkAcl(permission m.PermissionType, acl []*m.Dashb
} }
} }
// do we have group rules? // do we have team rules?
if len(teamAclItems) == 0 { if len(teamAclItems) == 0 {
return false, nil return false, nil
} }
// load groups // load teams
teams, err := g.getTeams() teams, err := g.getTeams()
if err != nil { if err != nil {
return false, err return false, err
} }
// evalute group rules // evalute team rules
for _, p := range acl { for _, p := range acl {
for _, ug := range teams { for _, ug := range teams {
if ug.Id == p.TeamId && p.Permission >= permission { if ug.Id == p.TeamId && p.Permission >= permission {
...@@ -140,7 +140,7 @@ func (g *DashboardGuardian) CheckPermissionBeforeUpdate(permission m.PermissionT ...@@ -140,7 +140,7 @@ func (g *DashboardGuardian) CheckPermissionBeforeUpdate(permission m.PermissionT
return g.checkAcl(permission, acl) return g.checkAcl(permission, acl)
} }
// Returns dashboard acl // GetAcl returns dashboard acl
func (g *DashboardGuardian) GetAcl() ([]*m.DashboardAclInfoDTO, error) { func (g *DashboardGuardian) GetAcl() ([]*m.DashboardAclInfoDTO, error) {
if g.acl != nil { if g.acl != nil {
return g.acl, nil return g.acl, nil
......
import React, { Component } from 'react'; import React, { Component } from 'react';
import { observer } from 'mobx-react';
import { store } from 'app/stores/store'; import { store } from 'app/stores/store';
import Permissions from 'app/core/components/Permissions/Permissions'; import Permissions from 'app/core/components/Permissions/Permissions';
...@@ -9,7 +8,6 @@ export interface IProps { ...@@ -9,7 +8,6 @@ export interface IProps {
backendSrv: any; backendSrv: any;
} }
@observer
class DashboardPermissions extends Component<IProps, any> { class DashboardPermissions extends Component<IProps, any> {
permissions: any; permissions: any;
......
import React, { Component } from 'react'; import React, { Component } from 'react';
import DescriptionPicker from 'app/core/components/Picker/DescriptionPicker';
import { permissionOptions } from 'app/stores/PermissionsStore/PermissionsStore'; import { permissionOptions } from 'app/stores/PermissionsStore/PermissionsStore';
export interface IProps { export interface IProps {
...@@ -18,16 +19,13 @@ export default class DisabledPermissionListItem extends Component<IProps, any> { ...@@ -18,16 +19,13 @@ export default class DisabledPermissionListItem extends Component<IProps, any> {
<td /> <td />
<td className="query-keyword">Can</td> <td className="query-keyword">Can</td>
<td> <td>
<div className="gf-form-select-wrapper"> <div className="gf-form">
<select value={item.permission} className="gf-form-input gf-size-auto" disabled={true}> <DescriptionPicker
{permissionOptions.map((option, idx) => { optionsWithDesc={permissionOptions}
return ( handlePicked={() => {}}
<option key={idx} value={option.value}> value={item.permission}
{option.text} disabled={true}
</option> />
);
})}
</select>
</div> </div>
</td> </td>
<td> <td>
......
...@@ -142,7 +142,7 @@ class Permissions extends Component<IProps, any> { ...@@ -142,7 +142,7 @@ class Permissions extends Component<IProps, any> {
</div> </div>
) : null} ) : null}
</div> </div>
<div className="empty-list-cta m-t-3"> {/* <div className="empty-list-cta m-t-3">
<div className="grafana-info-box"> <div className="grafana-info-box">
<h5>What are Permissions?</h5> <h5>What are Permissions?</h5>
<p> <p>
...@@ -157,7 +157,7 @@ class Permissions extends Component<IProps, any> { ...@@ -157,7 +157,7 @@ class Permissions extends Component<IProps, any> {
</a>{' '} </a>{' '}
for more information. for more information.
</div> </div>
</div> </div> */}
</div> </div>
); );
} }
......
import React from 'react'; import React from 'react';
import { observer } from 'mobx-react'; import { observer } from 'mobx-react';
import DescriptionPicker from 'app/core/components/Picker/DescriptionPicker';
import { permissionOptions } from 'app/stores/PermissionsStore/PermissionsStore'; import { permissionOptions } from 'app/stores/PermissionsStore/PermissionsStore';
const setClassNameHelper = inherited => { const setClassNameHelper = inherited => {
...@@ -12,12 +13,8 @@ export default observer(({ item, removeItem, permissionChanged, itemIndex, folde ...@@ -12,12 +13,8 @@ export default observer(({ item, removeItem, permissionChanged, itemIndex, folde
removeItem(itemIndex); removeItem(itemIndex);
}; };
const handleChangePermission = evt => { const handleChangePermission = permissionOption => {
evt.preventDefault(); permissionChanged(itemIndex, permissionOption.value, permissionOption.label);
const value = evt.target.value;
const valueAsInt = parseInt(value, 10);
const newPermission = permissionOptions.find(opt => opt.value === valueAsInt);
permissionChanged(itemIndex, newPermission.value, newPermission.text);
}; };
return ( return (
...@@ -29,21 +26,13 @@ export default observer(({ item, removeItem, permissionChanged, itemIndex, folde ...@@ -29,21 +26,13 @@ export default observer(({ item, removeItem, permissionChanged, itemIndex, folde
<td>{item.inherited ? <em className="muted no-wrap">Inherited from folder {folderTitle} </em> : null}</td> <td>{item.inherited ? <em className="muted no-wrap">Inherited from folder {folderTitle} </em> : null}</td>
<td className="query-keyword">Can</td> <td className="query-keyword">Can</td>
<td> <td>
<div className="gf-form-select-wrapper"> <div className="gf-form">
<select <DescriptionPicker
optionsWithDesc={permissionOptions}
handlePicked={handleChangePermission}
value={item.permission} value={item.permission}
className="gf-form-input gf-size-auto"
onChange={handleChangePermission}
disabled={item.inherited} disabled={item.inherited}
> />
{permissionOptions.map((option, idx) => {
return (
<option key={idx} value={option.value}>
{option.text}
</option>
);
})}
</select>
</div> </div>
</td> </td>
<td> <td>
......
import React, { Component } from 'react';
export interface IProps {
onSelect: any;
onFocus: any;
option: any;
isFocused: any;
className: any;
}
class DescriptionOption extends Component<IProps, any> {
constructor(props) {
super(props);
this.handleMouseDown = this.handleMouseDown.bind(this);
this.handleMouseEnter = this.handleMouseEnter.bind(this);
this.handleMouseMove = this.handleMouseMove.bind(this);
}
handleMouseDown(event) {
event.preventDefault();
event.stopPropagation();
this.props.onSelect(this.props.option, event);
}
handleMouseEnter(event) {
this.props.onFocus(this.props.option, event);
}
handleMouseMove(event) {
if (this.props.isFocused) {
return;
}
this.props.onFocus(this.props.option, event);
}
render() {
const { option, children, className } = this.props;
return (
<button
onMouseDown={this.handleMouseDown}
onMouseEnter={this.handleMouseEnter}
onMouseMove={this.handleMouseMove}
title={option.title}
className={`user-picker-option__button btn btn-link ${className} width-19`}
style={{
whiteSpace: 'normal',
// height: '55px',
}}
>
<div className="gf-form">{children}</div>
<div className="gf-form">
<div className="muted width-17">{option.description}</div>
{className.indexOf('is-selected') > -1 && (
<i style={{ paddingLeft: '2px' }} className="fa fa-check" aria-hidden="true" />
)}
</div>
</button>
);
}
}
export default DescriptionOption;
import React, { Component } from 'react';
import Select from 'react-select';
import DescriptionOption from './DescriptionOption';
export interface IProps {
optionsWithDesc: OptionWithDescription[];
handlePicked: (permission) => void;
value: number;
disabled: boolean;
}
export interface OptionWithDescription {
value: any;
label: string;
description: string;
}
class DescriptionPicker extends Component<IProps, any> {
constructor(props) {
super(props);
this.state = {};
}
render() {
const { optionsWithDesc, handlePicked, value, disabled } = this.props;
return (
<div className="permissions-picker">
<Select
value={value}
valueKey="value"
multi={false}
clearable={false}
labelKey="label"
options={optionsWithDesc}
onChange={handlePicked}
className="width-7 gf-form-input gf-form-input--form-dropdown"
optionComponent={DescriptionOption}
placeholder="Choose"
disabled={disabled}
/>
</div>
);
}
}
export default DescriptionPicker;
...@@ -3,7 +3,15 @@ import { PermissionsStoreItem } from './PermissionsStoreItem'; ...@@ -3,7 +3,15 @@ import { PermissionsStoreItem } from './PermissionsStoreItem';
const duplicateError = 'This permission exists already.'; const duplicateError = 'This permission exists already.';
export const permissionOptions = [{ value: 1, text: 'View' }, { value: 2, text: 'Edit' }, { value: 4, text: 'Admin' }]; export const permissionOptions = [
{ value: 1, label: 'View', description: 'Can view dashboards.' },
{ value: 2, label: 'Edit', description: 'Can add, edit and delete dashboards.' },
{
value: 4,
label: 'Admin',
description: 'Can add/remove permissions and can add, edit and delete dashboards.',
},
];
export const aclTypes = [ export const aclTypes = [
{ value: 'Group', text: 'Team' }, { value: 'Group', text: 'Team' },
......
...@@ -9,6 +9,9 @@ $select-noresults-color: $text-color; ...@@ -9,6 +9,9 @@ $select-noresults-color: $text-color;
$select-input-bg: $input-bg; $select-input-bg: $input-bg;
$select-input-border-color: $input-border-color; $select-input-border-color: $input-border-color;
$select-menu-box-shadow: $menu-dropdown-shadow; $select-menu-box-shadow: $menu-dropdown-shadow;
$select-text-color: $text-color;
$select-input-bg-disabled: $input-bg-disabled;
$select-option-selected-bg: $dropdownLinkBackgroundActive;
@import '../../../node_modules/react-select/scss/default.scss'; @import '../../../node_modules/react-select/scss/default.scss';
...@@ -36,6 +39,10 @@ $select-menu-box-shadow: $menu-dropdown-shadow; ...@@ -36,6 +39,10 @@ $select-menu-box-shadow: $menu-dropdown-shadow;
color: $input-color-placeholder; color: $input-color-placeholder;
} }
.Select-menu-outer {
right: 0;
}
> .Select-control { > .Select-control {
@include select-control(); @include select-control();
border-color: $dark-3; border-color: $dark-3;
...@@ -51,15 +58,11 @@ $select-menu-box-shadow: $menu-dropdown-shadow; ...@@ -51,15 +58,11 @@ $select-menu-box-shadow: $menu-dropdown-shadow;
.Select-value { .Select-value {
display: inline-block; display: inline-block;
padding: 2px 4px; padding: $input-padding-y $input-padding-x;
font-size: $font-size-base * 0.846; font-size: $font-size-md;
font-weight: bold; line-height: $input-line-height;
line-height: 14px; // ensure proper line-height if floated
color: $white;
vertical-align: baseline; vertical-align: baseline;
white-space: nowrap; white-space: nowrap;
text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
background-color: $gray-1;
} }
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment