-
Notifications
You must be signed in to change notification settings - Fork 18
Create space user's dropdown form for adding other org users #1172
Changes from 21 commits
e56bc8c
4ac8ee1
503e7f4
0bba2f7
931eef7
9fa5cbc
2461e0c
651e522
9c2815e
fa73119
04a8972
1d3f87f
55defcd
33a9778
4947863
42cb434
a94ab91
06ad595
02106fb
b98bf11
a7813cf
80ca7d6
fe47005
38ed848
ff12ede
5d8b81d
57ac1d0
339846c
f655f6e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
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, | ||
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 OrgUsersSelector 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 apiKey = AUDITOR_NAME; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should this be renamed to There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. i like the idea of renaming it! let's move it to (coming from me, the person who introduced the pattern lol) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. there's another refactor while we are talking about it, (but outside the scope of this PR) This needs to be made singular. Also, would like to rename |
||
const roles = SPACE_AUDITOR_NAME; | ||
if (values.userGuid) { | ||
const userGuid = values.userGuid.value; | ||
userActions.addUserRoles(roles, apiKey, 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-invite"> | ||
<PanelDocumentation description> | ||
<p>{ this.invitationMessage }</p> | ||
</PanelDocumentation> | ||
<Form | ||
guid={ USERS_PARENT_ENTITY_USER_FORM_GUID } | ||
classes={ ['users_parent_entity_user_form'] } | ||
ref="form" | ||
onSubmit={ this._onSubmitForm } | ||
> | ||
{ this.userSelector } | ||
<Action | ||
label="submit" | ||
type="submit" | ||
disabled={ orgUsersSelectorDisabled } | ||
> | ||
Add user to this { currentEntity } | ||
</Action> | ||
</Form> | ||
</div> | ||
); | ||
} | ||
|
||
} | ||
|
||
OrgUsersSelector.propTypes = propTypes; | ||
|
||
OrgUsersSelector.defaultProps = defaultProps; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
import '../../global_setup.js'; | ||
|
||
import React from 'react'; | ||
import { shallow } from 'enzyme'; | ||
import { Form, FormSelect } from '../../../components/form'; | ||
import OrgUsersSelector from '../../../components/users_org_user_selector.jsx'; | ||
import PanelDocumentation from '../../../components/panel_documentation.jsx'; | ||
|
||
describe('<OrgUsersSelector />', function () { | ||
const parentEntityType = 'organization'; | ||
const entityType = 'space'; | ||
const props = { | ||
currentUserAccess: true, | ||
orgUsersSelectorDisabled: false, | ||
parentEntityUsers: [], | ||
error: {}, | ||
parentEntity: parentEntityType, | ||
currentEntityGuid: 'a-space-guid', | ||
currentEntity: entityType | ||
}; | ||
let wrapper; | ||
|
||
|
||
describe('when the working description is displayed as text panel', () => { | ||
beforeEach(() => { | ||
wrapper = shallow(<OrgUsersSelector { ...props } />); | ||
}); | ||
|
||
it('displays proper message', () => { | ||
const doc = 'Invite an existing user in this organization to this space.'; | ||
expect(wrapper.find(PanelDocumentation).find('p').text()).toBe(doc); | ||
}); | ||
}); | ||
|
||
describe('when user selector', () => { | ||
it('renders users', () => { | ||
const username = 'username'; | ||
const guid = 'a-guid'; | ||
const user = { guid, username }; | ||
const parentEntityUsers = [user, user, user]; | ||
const usersProps = Object.assign({}, props, { parentEntityUsers }); | ||
wrapper = shallow(<OrgUsersSelector { ...usersProps } />); | ||
const formSelect = wrapper.find(Form).find(FormSelect); | ||
expect(formSelect.length).toEqual(1); | ||
expect(formSelect.props().options.length).toEqual(3); | ||
}); | ||
it('renders without users', () => { | ||
const usersProps = Object.assign({}, props, { parentEntityUsers: [] }); | ||
wrapper = shallow(<OrgUsersSelector { ...usersProps } />); | ||
const formSelect = wrapper.find(Form).find(FormSelect); | ||
expect(formSelect.length).toEqual(1); | ||
expect(formSelect.props().options.length).toEqual(0); | ||
}); | ||
}); | ||
|
||
describe('when user does not have ability to invite other users', () => { | ||
it('does not render <Form /> component', () => { | ||
const noAccessProps = Object.assign({}, props, { currentUserAccess: false }); | ||
wrapper = shallow(<OrgUsersSelector { ...noAccessProps } />); | ||
|
||
expect(wrapper.find(Form).length).toEqual(0); | ||
}); | ||
}); | ||
}); |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -682,6 +682,20 @@ describe('UserStore', function () { | |
}); | ||
}); | ||
|
||
describe('getAllInOrgAndNotSpace()', function() { | ||
it('should find all users that are in an org, without the space users', function() { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I am probably misreading this spec, but from the description, shouldn't the Should the corresponding method in |
||
var spaceGuid = 'sdfadf'; | ||
var orgGuid = 'asdfa'; | ||
var testUser = { guid: 'adfzxcv', roles: { [orgGuid]: [ 'org_user'] } }; | ||
|
||
UserStore.push(testUser); | ||
|
||
let actual = UserStore.getAllInOrgAndNotSpace(spaceGuid); | ||
|
||
expect(actual[0]).toEqual(testUser); | ||
}); | ||
}); | ||
|
||
describe('USER_FETCH', function () { | ||
let user; | ||
beforeEach(function () { | ||
|
@@ -909,6 +923,7 @@ describe('UserStore', function () { | |
}); | ||
}); | ||
}); | ||
|
||
describe('isAdmin()', function () { | ||
describe('user with _currentUserIsAdmin', function () { | ||
let user, space, org, actual; | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this component has a lot of attributes. for a lot of attributes, you can make this generic like
UserSelector
.For OrgUserSelector, you probably wouldn't need all this. I'm leaning to keeping all of these and just rename to
UserSelector
and we can refactor for more generic cases later on.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
On it!