Skip to content
This repository has been archived by the owner on May 19, 2020. It is now read-only.

Create space user's dropdown form for adding other org users #1172

Merged
merged 29 commits into from
Aug 1, 2017
Merged
Show file tree
Hide file tree
Changes from 24 commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
e56bc8c
Add new constants to check for user role by username
rememberlenny Jul 28, 2017
4ac8ee1
Create ability to get all users in org that aren't in space
rememberlenny Jul 28, 2017
503e7f4
Create functions used to check space org role status
rememberlenny Jul 28, 2017
0bba2f7
Create the component to manage the org parent user selector
rememberlenny Jul 28, 2017
931eef7
Add the selector to the user component
rememberlenny Jul 28, 2017
9fa5cbc
Get the selector based user adding working
rememberlenny Jul 28, 2017
2461e0c
Add a component test
rememberlenny Jul 28, 2017
651e522
Remove unused store cases
rememberlenny Jul 28, 2017
9c2815e
Create check again space users
rememberlenny Jul 28, 2017
fa73119
Take care of linting
rememberlenny Jul 28, 2017
04a8972
Lint tests
rememberlenny Jul 28, 2017
1d3f87f
Fix linting
rememberlenny Jul 28, 2017
55defcd
Remove unused comment
rememberlenny Jul 28, 2017
33a9778
Rename component to be easily grokable
rememberlenny Jul 28, 2017
4947863
Pull out constants
rememberlenny Jul 28, 2017
42cb434
Rework file names and component reference
rememberlenny Jul 28, 2017
a94ab91
Get the error working on the component
rememberlenny Jul 28, 2017
06ad595
Get the working selector check
rememberlenny Jul 28, 2017
02106fb
Merge branch 'master' of github.com:18F/cg-dashboard into lkb-dropdow…
rememberlenny Jul 28, 2017
b98bf11
Save the changes on the user store check
rememberlenny Jul 28, 2017
a7813cf
Fix linting
rememberlenny Jul 28, 2017
80ca7d6
Add the selector detection
rememberlenny Aug 1, 2017
fe47005
Rename the component
rememberlenny Aug 1, 2017
38ed848
Change the function argument names
rememberlenny Aug 1, 2017
ff12ede
Rename usersSelectorDisabled
rememberlenny Aug 1, 2017
5d8b81d
Linting
rememberlenny Aug 1, 2017
57ac1d0
Re-update user action names
rememberlenny Aug 1, 2017
339846c
Fix new var name on params around addUserRoles
rememberlenny Aug 1, 2017
f655f6e
Rename classes for specificity
rememberlenny Aug 1, 2017
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 8 additions & 6 deletions static_src/actions/user_actions.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ import UserStore from '../stores/user_store';
import OrgStore from '../stores/org_store';
import SpaceStore from '../stores/space_store';

const ORG_ENTITY = 'organization';
const SPACE_ENTITY = 'space';
const ORG_NAME = OrgStore.cfName;
const MSG_USER_HAS_SPACE_ROLES = 'This user can\'t be removed because they still have a space ' +
'role within the organization. Please remove all space ' +
Expand Down Expand Up @@ -146,7 +148,7 @@ const userActions = {
});
},

addUserRoles(roles, apiKey, userGuid, entityGuid, entityType) {
addUserRoles(resource_role_name, user_role, userGuid, entityGuid, entityType) {
Copy link
Contributor

@jcscottiii jcscottiii Aug 1, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ahh sorry for the confusing conversation.
we were saying apiKey -> resource
and you can revert resource_role_name to roles and leave roles as-is for now.
we will address the naming when we address addUserRoles and addedUserRoles and the mapping to the frontend roles

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should stick to camel case as well

const apiMethodMap = {
organization: cfApi.putOrgUserPermissions,
space: cfApi.putSpaceUserPermissions
Expand All @@ -155,7 +157,7 @@ const userActions = {

AppDispatcher.handleViewAction({
type: userActionTypes.USER_ROLES_ADD,
roles,
resource_role_name,
userGuid,
entityGuid,
entityType
Expand All @@ -164,10 +166,10 @@ const userActions = {
return api(
userGuid,
entityGuid,
apiKey
user_role
).then(() => {
userActions.addedUserRoles(
roles,
resource_role_name,
userGuid,
entityGuid,
entityType);
Expand Down Expand Up @@ -291,8 +293,8 @@ const userActions = {
const noticeType = 'finish';
const currentViewedType = UserStore.currentlyViewedType;
const viewTypeNouns = Object.assign({},
{ space_users: { singular: 'space' } },
{ org_users: { singular: 'organization' } }
{ space_users: { singular: SPACE_ENTITY } },
{ org_users: { singular: ORG_ENTITY } }
);
const entity = viewTypeNouns[currentViewedType].singular;

Expand Down
41 changes: 41 additions & 0 deletions static_src/components/users.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import OrgStore from '../stores/org_store.js';
import SpaceStore from '../stores/space_store.js';
import UserList from './user_list.jsx';
import UsersInvite from './users_invite.jsx';
import UsersSelector from './users_selector.jsx';
import Notification from './notification.jsx';
import UserStore from '../stores/user_store.js';
import ErrorMessage from './error_message.jsx';
Expand All @@ -33,12 +34,15 @@ function stateSetter() {
const isSaving = UserStore.isSaving;

let users = [];
let parentEntityUsers;
let currentUserAccess = false;
const inviteDisabled = UserStore.inviteDisabled();
const orgUsersSelectorDisabled = UserStore.orgUsersSelectorDisabled();
let entityGuid;

if (currentType === SPACE_NAME) {
users = UserStore.getAllInSpace(currentSpaceGuid);
parentEntityUsers = UserStore.getAllInOrgAndNotSpace(currentSpaceGuid);
entityGuid = currentSpaceGuid;
currentUserAccess = UserStore.hasRole(currentUser.guid, currentSpaceGuid,
SPACE_MANAGER);
Expand All @@ -53,6 +57,7 @@ function stateSetter() {
currentUser,
error: UserStore.getError(),
inviteDisabled,
orgUsersSelectorDisabled,
currentUserAccess,
currentOrgGuid,
currentSpaceGuid,
Expand All @@ -62,6 +67,7 @@ function stateSetter() {
loading: UserStore.loading,
empty: !UserStore.loading && !users.length,
users,
parentEntityUsers,
userListNotices: UserStore._userListNotification,
userListNoticeError: UserStore.getUserListNotificationError()
};
Expand Down Expand Up @@ -140,6 +146,13 @@ export default class Users extends React.Component {
return entityGuid;
}

get currentUserIsSpaceManager() {
const { currentUser } = this.state;
const { currentSpaceGuid } = SpaceStore;

return UserStore.hasRole(currentUser.guid, currentSpaceGuid, SPACE_MANAGER);
}

get currentUserIsOrgManager() {
const { currentUser } = this.state;
const { currentOrgGuid } = OrgStore;
Expand Down Expand Up @@ -186,6 +199,33 @@ export default class Users extends React.Component {
);
}

get userParentEntityUserSelector() {
if (!this.isSpace) {
return null;
}

if (!this.currentUserIsSpaceManager) {
return (
<PanelDocumentation>
Org Managers and Space Managers can add current organization users into this space.
</PanelDocumentation>
);
}

return (
<UsersSelector
orgUsersSelectorDisabled={ this.state.orgUsersSelectorDisabled }
parentEntity={ ORG_ENTITY }
currentEntityGuid={ this.entityGuid }
currentEntity={ this.entityType }
parentEntityUsers={ this.state.parentEntityUsers }
inviteEntityType={ this.entityType }
currentUserAccess={ this.state.currentUserAccess }
error={ this.state.userListNoticeError }
/>
);
}

_onChange() {
this.setState(stateSetter());
}
Expand All @@ -203,6 +243,7 @@ export default class Users extends React.Component {
<div className="test-users">
<ErrorMessage error={this.state.error} />
{ this.userInvite }
{ this.userParentEntityUserSelector }
{ this.notification }
<UserList
users={ this.state.users }
Expand Down
123 changes: 123 additions & 0 deletions static_src/components/users_selector.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
/**
* Renders a form that allows org users to invite new users
* to cloud.gov
*/

import PropTypes from 'prop-types';
import React from 'react';
import Action from './action.jsx';
import FormStore from '../stores/form_store';
import { Form, FormSelect } from './form';
import PanelDocumentation from './panel_documentation.jsx';
import userActions from '../actions/user_actions';
import { validateString } from '../util/validators';

const AUDITOR_NAME = 'auditors';
const SPACE_AUDITOR_NAME = 'space_auditor';
const USERS_PARENT_ENTITY_USER_FORM_GUID = 'users-parent-entity-users-form';

const propTypes = {
orgUsersSelectorDisabled: PropTypes.bool,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this can be renamed to selectorDisabled or usersSelectorDisabled

currentUserAccess: PropTypes.bool,
parentEntityUsers: PropTypes.array,
error: PropTypes.object,
parentEntity: PropTypes.string,
currentEntityGuid: PropTypes.string,
currentEntity: PropTypes.string
};
const defaultProps = {
orgUsersSelectorDisabled: false,
currentUserAccess: false,
error: {}
};

export default class UsersSelector extends React.Component {
constructor(props) {
super(props);

this.validateString = validateString().bind(this);
this._onSubmitForm = this._onSubmitForm.bind(this);
}

componentDidMount() {
FormStore.create(USERS_PARENT_ENTITY_USER_FORM_GUID);
}

_onSubmitForm(errs, values) {
const { currentEntity } = this.props;
const { currentEntityGuid } = this.props;
const user_role = AUDITOR_NAME;
const resource_role_name = SPACE_AUDITOR_NAME;
if (values.userGuid) {
const userGuid = values.userGuid.value;
userActions.addUserRoles(resource_role_name, user_role, userGuid, currentEntityGuid, currentEntity);
}
}

get invitationMessage() {
const { parentEntity } = this.props;
const { currentEntity } = this.props;

return `Invite an existing user in this ${parentEntity}` +
` to this ${currentEntity}.`;
}

get userSelector() {
const { parentEntityUsers } = this.props;
const orgUsers = parentEntityUsers.map((user) =>
({ value: user.guid, label: user.username })
);

if (!orgUsers) {
return null;
}

return (
<FormSelect
formGuid={ USERS_PARENT_ENTITY_USER_FORM_GUID }
classes={ ['test-users'] }
label="Username"
name="userGuid"
options={ orgUsers }
validator={ this.validateString }
/>
);
}

render() {
const { orgUsersSelectorDisabled } = this.props;
const { currentEntity } = this.props;

if (!this.props.currentUserAccess) {
return null;
}

return (
<div className="test-users-selector">
<PanelDocumentation description>
<p>{ this.invitationMessage }</p>
</PanelDocumentation>
<Form
guid={ USERS_PARENT_ENTITY_USER_FORM_GUID }
classes={ ['org_user_selector'] }
ref="form"
onSubmit={ this._onSubmitForm }
>
{ this.userSelector }
<Action
label="submit"
type="submit"
disabled={ orgUsersSelectorDisabled }
>
Add user to this { currentEntity }
</Action>
</Form>
</div>
);
}

}

UsersSelector.propTypes = propTypes;

UsersSelector.defaultProps = defaultProps;
15 changes: 14 additions & 1 deletion static_src/stores/user_store.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ export class UserStore extends BaseStore {
this._error = null;
this._saving = false;
this._inviteDisabled = false;
this._orgUsersSelectorDisabled = false;
this._userListNotification = {};
this._loading = {};
}
Expand Down Expand Up @@ -189,7 +190,7 @@ export class UserStore extends BaseStore {
this._userListNotificationError = Object.assign({}, action.err, {
contextualMessage: action.contextualMessage
});
this._inviteDisabled = false;
this._orgUsersSelectorDisabled = false;
this.emitChange();
break;
}
Expand Down Expand Up @@ -322,6 +323,14 @@ export class UserStore extends BaseStore {
return usersInOrg.toJS();
}

getAllInOrgAndNotSpace() {
const usersInOrg = this._data.toJS().filter((user) =>
!user.space_roles
);

return usersInOrg;
}

getError() {
return this._error;
}
Expand Down Expand Up @@ -367,6 +376,10 @@ export class UserStore extends BaseStore {
return !!roles.find((role) => wrappedRoles.includes(role));
}

orgUsersSelectorDisabled() {
return this._orgUsersSelectorDisabled;
}

inviteDisabled() {
return this._inviteDisabled;
}
Expand Down
6 changes: 6 additions & 0 deletions static_src/test/functional/pageobjects/user_invite.element.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,12 @@ export default class UserInviteElement extends BaseElement {
return browser.elements('.test-users .complex_list-item').value.length;
}

// TODO move this to user list element.
countNumberOfUserSelectors() {
browser.waitForExist('.test-users-selector');
return browser.elements('.test-users-selector').value.length;
}

// TODO move this to user list element.
getUserByIndex(idx) {
const sel = `.test-users .complex_list-item:nth-child(${idx})`;
Expand Down
8 changes: 3 additions & 5 deletions static_src/test/functional/user_invite.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -66,15 +66,13 @@ describe('User roles', function () {
browser.waitForExist('.test-users');
});

it('should not have the user invite panel', function () {
it('should have the user selector panel', function () {
userRoleElement.setAndGetUserRole(cookieManagerOrgXSpaceXX);
browser.url(urlOrgXSpaceXX);

browser.waitForExist('.test-users');
browser.waitForExist('.test-users-invite', 500, true);
const ells = browser.elements('.test-users-invite');
const userSelectorCount = userInviteElement.countNumberOfUserSelectors();

expect(ells.length).toBe(undefined);
expect(userSelectorCount).toBe(1);
});
});
});
Loading