Commit 96aa4ae1 by Hugo Häggmark Committed by Leonard Gram

teams: remov permission select for non admin users

parent b9cf09a2
...@@ -12,6 +12,7 @@ const setup = (propOverrides?: object) => { ...@@ -12,6 +12,7 @@ const setup = (propOverrides?: object) => {
{ {
link: {}, link: {},
user: { user: {
id: 1,
isGrafanaAdmin: false, isGrafanaAdmin: false,
isSignedIn: false, isSignedIn: false,
orgCount: 2, orgCount: 2,
......
...@@ -3,6 +3,7 @@ import _ from 'lodash'; ...@@ -3,6 +3,7 @@ import _ from 'lodash';
import coreModule from 'app/core/core_module'; import coreModule from 'app/core/core_module';
export class User { export class User {
id: number;
isGrafanaAdmin: any; isGrafanaAdmin: any;
isSignedIn: any; isSignedIn: any;
orgRole: any; orgRole: any;
......
...@@ -4,8 +4,25 @@ import { TeamMembers, Props, State } from './TeamMembers'; ...@@ -4,8 +4,25 @@ import { TeamMembers, Props, State } from './TeamMembers';
import { TeamMember, TeamPermissionLevel } from '../../types'; import { TeamMember, TeamPermissionLevel } from '../../types';
import { getMockTeamMember, getMockTeamMembers } from './__mocks__/teamMocks'; import { getMockTeamMember, getMockTeamMembers } from './__mocks__/teamMocks';
import { SelectOptionItem } from '@grafana/ui'; import { SelectOptionItem } from '@grafana/ui';
import { contextSrv } from 'app/core/services/context_srv';
const setup = (propOverrides?: object) => { jest.mock('app/core/services/context_srv', () => ({
contextSrv: {
isGrafanaAdmin: false,
hasRole: role => false,
user: { id: 1 },
},
}));
const originalContextSrv = contextSrv;
interface SetupProps {
propOverrides?: object;
isGrafanaAdmin?: boolean;
isOrgAdmin?: boolean;
}
const setup = (setupProps: SetupProps) => {
const props: Props = { const props: Props = {
members: [] as TeamMember[], members: [] as TeamMember[],
searchMemberQuery: '', searchMemberQuery: '',
...@@ -15,9 +32,13 @@ const setup = (propOverrides?: object) => { ...@@ -15,9 +32,13 @@ const setup = (propOverrides?: object) => {
removeTeamMember: jest.fn(), removeTeamMember: jest.fn(),
updateTeamMember: jest.fn(), updateTeamMember: jest.fn(),
syncEnabled: false, syncEnabled: false,
editorsCanAdmin: false,
}; };
Object.assign(props, propOverrides); contextSrv.isGrafanaAdmin = setupProps.isGrafanaAdmin || false;
contextSrv.hasRole = role => setupProps.isOrgAdmin || false;
Object.assign(props, setupProps.propOverrides);
const wrapper = shallow(<TeamMembers {...props} />); const wrapper = shallow(<TeamMembers {...props} />);
const instance = wrapper.instance() as TeamMembers; const instance = wrapper.instance() as TeamMembers;
...@@ -29,15 +50,22 @@ const setup = (propOverrides?: object) => { ...@@ -29,15 +50,22 @@ const setup = (propOverrides?: object) => {
}; };
describe('Render', () => { describe('Render', () => {
beforeEach(() => {
contextSrv.isGrafanaAdmin = originalContextSrv.isGrafanaAdmin;
contextSrv.hasRole = originalContextSrv.hasRole;
});
it('should render component', () => { it('should render component', () => {
const { wrapper } = setup(); const { wrapper } = setup({});
expect(wrapper).toMatchSnapshot(); expect(wrapper).toMatchSnapshot();
}); });
it('should render team members', () => { it('should render team members', () => {
const { wrapper } = setup({ const { wrapper } = setup({
propOverrides: {
members: getMockTeamMembers(5), members: getMockTeamMembers(5),
},
}); });
expect(wrapper).toMatchSnapshot(); expect(wrapper).toMatchSnapshot();
...@@ -45,18 +73,58 @@ describe('Render', () => { ...@@ -45,18 +73,58 @@ describe('Render', () => {
it('should render team members when sync enabled', () => { it('should render team members when sync enabled', () => {
const { wrapper } = setup({ const { wrapper } = setup({
propOverrides: {
members: getMockTeamMembers(5), members: getMockTeamMembers(5),
syncEnabled: true, syncEnabled: true,
},
}); });
expect(wrapper).toMatchSnapshot(); expect(wrapper).toMatchSnapshot();
}); });
describe('when feature toggle editorsCanAdmin is turned on', () => {
it('should render permissions select if user is Grafana Admin', () => {
const members = getMockTeamMembers(5);
members[4].permission = TeamPermissionLevel.Admin;
const { wrapper } = setup({
propOverrides: { members, editorsCanAdmin: true },
isGrafanaAdmin: true,
isOrgAdmin: false,
});
expect(wrapper).toMatchSnapshot();
});
it('should render permissions select if user is Org Admin', () => {
const members = getMockTeamMembers(5);
members[4].permission = TeamPermissionLevel.Admin;
const { wrapper } = setup({
propOverrides: { members, editorsCanAdmin: true },
isGrafanaAdmin: false,
isOrgAdmin: true,
});
expect(wrapper).toMatchSnapshot();
});
it('should render permissions select if user is team admin', () => {
const members = getMockTeamMembers(5);
members[0].permission = TeamPermissionLevel.Admin;
const { wrapper } = setup({
propOverrides: { members, editorsCanAdmin: true },
isGrafanaAdmin: false,
isOrgAdmin: false,
});
expect(wrapper).toMatchSnapshot();
});
});
}); });
describe('Functions', () => { describe('Functions', () => {
describe('on search member query change', () => { describe('on search member query change', () => {
it('it should call setSearchMemberQuery', () => { it('it should call setSearchMemberQuery', () => {
const { instance } = setup(); const { instance } = setup({});
instance.onSearchQueryChange('member'); instance.onSearchQueryChange('member');
...@@ -65,7 +133,7 @@ describe('Functions', () => { ...@@ -65,7 +133,7 @@ describe('Functions', () => {
}); });
describe('on remove member', () => { describe('on remove member', () => {
const { instance } = setup(); const { instance } = setup({});
const mockTeamMember = getMockTeamMember(); const mockTeamMember = getMockTeamMember();
instance.onRemoveMember(mockTeamMember); instance.onRemoveMember(mockTeamMember);
...@@ -74,7 +142,7 @@ describe('Functions', () => { ...@@ -74,7 +142,7 @@ describe('Functions', () => {
}); });
describe('on add user to team', () => { describe('on add user to team', () => {
const { wrapper, instance } = setup(); const { wrapper, instance } = setup({});
const state = wrapper.state() as State; const state = wrapper.state() as State;
state.newTeamMember = { state.newTeamMember = {
...@@ -90,7 +158,7 @@ describe('Functions', () => { ...@@ -90,7 +158,7 @@ describe('Functions', () => {
}); });
describe('on update permision for user in team', () => { describe('on update permision for user in team', () => {
const { instance } = setup(); const { instance } = setup({});
const permission = TeamPermissionLevel.Admin; const permission = TeamPermissionLevel.Admin;
const item: SelectOptionItem = { value: permission }; const item: SelectOptionItem = { value: permission };
const member: TeamMember = { const member: TeamMember = {
......
...@@ -4,7 +4,7 @@ import SlideDown from 'app/core/components/Animations/SlideDown'; ...@@ -4,7 +4,7 @@ import SlideDown from 'app/core/components/Animations/SlideDown';
import { UserPicker } from 'app/core/components/Select/UserPicker'; import { UserPicker } from 'app/core/components/Select/UserPicker';
import { DeleteButton, Select, SelectOptionItem } from '@grafana/ui'; import { DeleteButton, Select, SelectOptionItem } from '@grafana/ui';
import { TagBadge } from 'app/core/components/TagFilter/TagBadge'; import { TagBadge } from 'app/core/components/TagFilter/TagBadge';
import { TeamMember, User, teamsPermissionLevels } from 'app/types'; import { TeamMember, User, teamsPermissionLevels, TeamPermissionLevel, OrgRole } from 'app/types';
import { import {
loadTeamMembers, loadTeamMembers,
addTeamMember, addTeamMember,
...@@ -16,6 +16,7 @@ import { getSearchMemberQuery, getTeamMembers } from './state/selectors'; ...@@ -16,6 +16,7 @@ import { getSearchMemberQuery, getTeamMembers } from './state/selectors';
import { FilterInput } from 'app/core/components/FilterInput/FilterInput'; import { FilterInput } from 'app/core/components/FilterInput/FilterInput';
import { WithFeatureToggle } from 'app/core/components/WithFeatureToggle'; import { WithFeatureToggle } from 'app/core/components/WithFeatureToggle';
import { config } from 'app/core/config'; import { config } from 'app/core/config';
import { contextSrv } from 'app/core/services/context_srv';
export interface Props { export interface Props {
members: TeamMember[]; members: TeamMember[];
...@@ -26,6 +27,7 @@ export interface Props { ...@@ -26,6 +27,7 @@ export interface Props {
setSearchMemberQuery: typeof setSearchMemberQuery; setSearchMemberQuery: typeof setSearchMemberQuery;
updateTeamMember: typeof updateTeamMember; updateTeamMember: typeof updateTeamMember;
syncEnabled: boolean; syncEnabled: boolean;
editorsCanAdmin?: boolean;
} }
export interface State { export interface State {
...@@ -37,6 +39,7 @@ export class TeamMembers extends PureComponent<Props, State> { ...@@ -37,6 +39,7 @@ export class TeamMembers extends PureComponent<Props, State> {
constructor(props) { constructor(props) {
super(props); super(props);
this.state = { isAdding: false, newTeamMember: null }; this.state = { isAdding: false, newTeamMember: null };
this.renderPermissionsSelect = this.renderPermissionsSelect.bind(this);
} }
componentDidMount() { componentDidMount() {
...@@ -85,27 +88,44 @@ export class TeamMembers extends PureComponent<Props, State> { ...@@ -85,27 +88,44 @@ export class TeamMembers extends PureComponent<Props, State> {
this.props.updateTeamMember(updatedTeamMember); this.props.updateTeamMember(updatedTeamMember);
}; };
renderMember(member: TeamMember, syncEnabled: boolean) { renderPermissionsSelect(member: TeamMember) {
const { members, editorsCanAdmin } = this.props;
const userInMembers = members.find(m => m.userId === contextSrv.user.id);
const isUserTeamAdmin =
contextSrv.isGrafanaAdmin || contextSrv.hasRole(OrgRole.Admin)
? true
: userInMembers && userInMembers.permission === TeamPermissionLevel.Admin;
const value = teamsPermissionLevels.find(dp => dp.value === member.permission);
return ( return (
<tr key={member.userId}> <WithFeatureToggle featureToggle={editorsCanAdmin}>
<td className="width-4 text-center">
<img className="filter-table__avatar" src={member.avatarUrl} />
</td>
<td>{member.login}</td>
<td>{member.email}</td>
<WithFeatureToggle featureToggle={config.editorsCanAdmin}>
<td> <td>
<div className="gf-form"> <div className="gf-form">
{isUserTeamAdmin && (
<Select <Select
isSearchable={false} isSearchable={false}
options={teamsPermissionLevels} options={teamsPermissionLevels}
onChange={item => this.onPermissionChange(item, member)} onChange={item => this.onPermissionChange(item, member)}
className="gf-form-select-box__control--menu-right" className="gf-form-select-box__control--menu-right"
value={teamsPermissionLevels.find(dp => dp.value === member.permission)} value={value}
/> />
)}
{!isUserTeamAdmin && <span>{value.label}</span>}
</div> </div>
</td> </td>
</WithFeatureToggle> </WithFeatureToggle>
);
}
renderMember(member: TeamMember, syncEnabled: boolean) {
return (
<tr key={member.userId}>
<td className="width-4 text-center">
<img className="filter-table__avatar" src={member.avatarUrl} />
</td>
<td>{member.login}</td>
<td>{member.email}</td>
{this.renderPermissionsSelect(member)}
{syncEnabled && this.renderLabels(member.labels)} {syncEnabled && this.renderLabels(member.labels)}
<td className="text-right"> <td className="text-right">
<DeleteButton onConfirm={() => this.onRemoveMember(member)} /> <DeleteButton onConfirm={() => this.onRemoveMember(member)} />
...@@ -116,7 +136,7 @@ export class TeamMembers extends PureComponent<Props, State> { ...@@ -116,7 +136,7 @@ export class TeamMembers extends PureComponent<Props, State> {
render() { render() {
const { isAdding } = this.state; const { isAdding } = this.state;
const { searchMemberQuery, members, syncEnabled } = this.props; const { searchMemberQuery, members, syncEnabled, editorsCanAdmin } = this.props;
return ( return (
<div> <div>
<div className="page-action-bar"> <div className="page-action-bar">
...@@ -161,7 +181,7 @@ export class TeamMembers extends PureComponent<Props, State> { ...@@ -161,7 +181,7 @@ export class TeamMembers extends PureComponent<Props, State> {
<th /> <th />
<th>Name</th> <th>Name</th>
<th>Email</th> <th>Email</th>
<WithFeatureToggle featureToggle={config.editorsCanAdmin}> <WithFeatureToggle featureToggle={editorsCanAdmin}>
<th>Permission</th> <th>Permission</th>
</WithFeatureToggle> </WithFeatureToggle>
{syncEnabled && <th />} {syncEnabled && <th />}
...@@ -180,6 +200,7 @@ function mapStateToProps(state) { ...@@ -180,6 +200,7 @@ function mapStateToProps(state) {
return { return {
members: getTeamMembers(state.team), members: getTeamMembers(state.team),
searchMemberQuery: getSearchMemberQuery(state.team), searchMemberQuery: getSearchMemberQuery(state.team),
editorsCanAdmin: config.editorsCanAdmin, // this makes the feature toggle mockable/controllable from tests,
}; };
} }
......
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