소스 검색

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.
Daniel Lee 8 년 전
부모
커밋
5ee2d1de05

+ 4 - 4
pkg/services/guardian/guardian.go

@@ -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 {
 		return false, nil
 	}
 
-	// load groups
+	// load teams
 	teams, err := g.getTeams()
 	if err != nil {
 		return false, err
 	}
 
-	// evalute group rules
+	// evalute team rules
 	for _, p := range acl {
 		for _, ug := range teams {
 			if ug.Id == p.TeamId && p.Permission >= permission {
@@ -140,7 +140,7 @@ func (g *DashboardGuardian) CheckPermissionBeforeUpdate(permission m.PermissionT
 	return g.checkAcl(permission, acl)
 }
 
-// Returns dashboard acl
+// GetAcl returns dashboard acl
 func (g *DashboardGuardian) GetAcl() ([]*m.DashboardAclInfoDTO, error) {
 	if g.acl != nil {
 		return g.acl, nil

+ 0 - 2
public/app/core/components/Permissions/DashboardPermissions.tsx

@@ -1,5 +1,4 @@
 import React, { Component } from 'react';
-import { observer } from 'mobx-react';
 import { store } from 'app/stores/store';
 import Permissions from 'app/core/components/Permissions/Permissions';
 
@@ -9,7 +8,6 @@ export interface IProps {
   backendSrv: any;
 }
 
-@observer
 class DashboardPermissions extends Component<IProps, any> {
   permissions: any;
 

+ 8 - 10
public/app/core/components/Permissions/DisabledPermissionsListItem.tsx

@@ -1,4 +1,5 @@
 import React, { Component } from 'react';
+import DescriptionPicker from 'app/core/components/Picker/DescriptionPicker';
 import { permissionOptions } from 'app/stores/PermissionsStore/PermissionsStore';
 
 export interface IProps {
@@ -18,16 +19,13 @@ export default class DisabledPermissionListItem extends Component<IProps, any> {
         <td />
         <td className="query-keyword">Can</td>
         <td>
-          <div className="gf-form-select-wrapper">
-            <select value={item.permission} className="gf-form-input gf-size-auto" disabled={true}>
-              {permissionOptions.map((option, idx) => {
-                return (
-                  <option key={idx} value={option.value}>
-                    {option.text}
-                  </option>
-                );
-              })}
-            </select>
+          <div className="gf-form">
+            <DescriptionPicker
+              optionsWithDesc={permissionOptions}
+              handlePicked={() => {}}
+              value={item.permission}
+              disabled={true}
+            />
           </div>
         </td>
         <td>

+ 2 - 2
public/app/core/components/Permissions/Permissions.tsx

@@ -142,7 +142,7 @@ class Permissions extends Component<IProps, any> {
             </div>
           ) : null}
         </div>
-        <div className="empty-list-cta m-t-3">
+        {/* <div className="empty-list-cta m-t-3">
           <div className="grafana-info-box">
             <h5>What are Permissions?</h5>
             <p>
@@ -157,7 +157,7 @@ class Permissions extends Component<IProps, any> {
             </a>{' '}
             for more information.
           </div>
-        </div>
+        </div> */}
       </div>
     );
   }

+ 8 - 19
public/app/core/components/Permissions/PermissionsListItem.tsx

@@ -1,5 +1,6 @@
 import React from 'react';
 import { observer } from 'mobx-react';
+import DescriptionPicker from 'app/core/components/Picker/DescriptionPicker';
 import { permissionOptions } from 'app/stores/PermissionsStore/PermissionsStore';
 
 const setClassNameHelper = inherited => {
@@ -12,12 +13,8 @@ export default observer(({ item, removeItem, permissionChanged, itemIndex, folde
     removeItem(itemIndex);
   };
 
-  const handleChangePermission = evt => {
-    evt.preventDefault();
-    const value = evt.target.value;
-    const valueAsInt = parseInt(value, 10);
-    const newPermission = permissionOptions.find(opt => opt.value === valueAsInt);
-    permissionChanged(itemIndex, newPermission.value, newPermission.text);
+  const handleChangePermission = permissionOption => {
+    permissionChanged(itemIndex, permissionOption.value, permissionOption.label);
   };
 
   return (
@@ -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 className="query-keyword">Can</td>
       <td>
-        <div className="gf-form-select-wrapper">
-          <select
+        <div className="gf-form">
+          <DescriptionPicker
+            optionsWithDesc={permissionOptions}
+            handlePicked={handleChangePermission}
             value={item.permission}
-            className="gf-form-input gf-size-auto"
-            onChange={handleChangePermission}
             disabled={item.inherited}
-          >
-            {permissionOptions.map((option, idx) => {
-              return (
-                <option key={idx} value={option.value}>
-                  {option.text}
-                </option>
-              );
-            })}
-          </select>
+          />
         </div>
       </td>
       <td>

+ 62 - 0
public/app/core/components/Picker/DescriptionOption.tsx

@@ -0,0 +1,62 @@
+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;

+ 47 - 0
public/app/core/components/Picker/DescriptionPicker.tsx

@@ -0,0 +1,47 @@
+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;

+ 9 - 1
public/app/stores/PermissionsStore/PermissionsStore.ts

@@ -3,7 +3,15 @@ import { PermissionsStoreItem } from './PermissionsStoreItem';
 
 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 = [
   { value: 'Group', text: 'Team' },

+ 10 - 7
public/sass/components/_form_select_box.scss

@@ -9,6 +9,9 @@ $select-noresults-color: $text-color;
 $select-input-bg: $input-bg;
 $select-input-border-color: $input-border-color;
 $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';
 
@@ -36,6 +39,10 @@ $select-menu-box-shadow: $menu-dropdown-shadow;
     color: $input-color-placeholder;
   }
 
+  .Select-menu-outer {
+    right: 0;
+  }
+
   > .Select-control {
     @include select-control();
     border-color: $dark-3;
@@ -51,15 +58,11 @@ $select-menu-box-shadow: $menu-dropdown-shadow;
 
     .Select-value {
       display: inline-block;
-      padding: 2px 4px;
-      font-size: $font-size-base * 0.846;
-      font-weight: bold;
-      line-height: 14px; // ensure proper line-height if floated
-      color: $white;
+      padding: $input-padding-y $input-padding-x;
+      font-size: $font-size-md;
+      line-height: $input-line-height;
       vertical-align: baseline;
       white-space: nowrap;
-      text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
-      background-color: $gray-1;
     }
   }