diff --git a/awx/ui_next/src/screens/Team/TeamAdd/TeamAdd.test.jsx b/awx/ui_next/src/screens/Team/TeamAdd/TeamAdd.test.jsx
index ca8c7caa56..20eb762710 100644
--- a/awx/ui_next/src/screens/Team/TeamAdd/TeamAdd.test.jsx
+++ b/awx/ui_next/src/screens/Team/TeamAdd/TeamAdd.test.jsx
@@ -12,6 +12,7 @@ describe('', () => {
const updatedTeamData = {
name: 'new name',
description: 'new description',
+ organization: 1,
};
wrapper.find('TeamForm').prop('handleSubmit')(updatedTeamData);
expect(TeamsAPI.create).toHaveBeenCalledWith(updatedTeamData);
@@ -40,11 +41,18 @@ describe('', () => {
const teamData = {
name: 'new name',
description: 'new description',
+ organization: 1,
};
TeamsAPI.create.mockResolvedValueOnce({
data: {
id: 5,
...teamData,
+ summary_fields: {
+ organization: {
+ id: 1,
+ name: 'Default',
+ },
+ },
},
});
const wrapper = mountWithContexts(, {
diff --git a/awx/ui_next/src/screens/Team/TeamEdit/TeamEdit.jsx b/awx/ui_next/src/screens/Team/TeamEdit/TeamEdit.jsx
index a8c35e7039..a6580b4ce9 100644
--- a/awx/ui_next/src/screens/Team/TeamEdit/TeamEdit.jsx
+++ b/awx/ui_next/src/screens/Team/TeamEdit/TeamEdit.jsx
@@ -13,7 +13,6 @@ class TeamEdit extends Component {
super(props);
this.handleSubmit = this.handleSubmit.bind(this);
- this.submitInstanceGroups = this.submitInstanceGroups.bind(this);
this.handleCancel = this.handleCancel.bind(this);
this.handleSuccess = this.handleSuccess.bind(this);
@@ -22,11 +21,10 @@ class TeamEdit extends Component {
};
}
- async handleSubmit(values, groupsToAssociate, groupsToDisassociate) {
+ async handleSubmit(values) {
const { team } = this.props;
try {
await TeamsAPI.update(team.id, values);
- await this.submitInstanceGroups(groupsToAssociate, groupsToDisassociate);
this.handleSuccess();
} catch (err) {
this.setState({ error: err });
@@ -49,24 +47,6 @@ class TeamEdit extends Component {
history.push(`/teams/${id}/details`);
}
- async submitInstanceGroups(groupsToAssociate, groupsToDisassociate) {
- const { team } = this.props;
- try {
- await Promise.all(
- groupsToAssociate.map(id =>
- TeamsAPI.associateInstanceGroup(team.id, id)
- )
- );
- await Promise.all(
- groupsToDisassociate.map(id =>
- TeamsAPI.disassociateInstanceGroup(team.id, id)
- )
- );
- } catch (err) {
- this.setState({ error: err });
- }
- }
-
render() {
const { team } = this.props;
const { error } = this.state;
diff --git a/awx/ui_next/src/screens/Team/TeamList/TeamListItem.jsx b/awx/ui_next/src/screens/Team/TeamList/TeamListItem.jsx
index c77ed7dd31..73ddbcad14 100644
--- a/awx/ui_next/src/screens/Team/TeamList/TeamListItem.jsx
+++ b/awx/ui_next/src/screens/Team/TeamList/TeamListItem.jsx
@@ -1,15 +1,20 @@
import React, { Fragment } from 'react';
import { string, bool, func } from 'prop-types';
import { withI18n } from '@lingui/react';
+import { t } from '@lingui/macro';
import {
DataListItem,
DataListItemRow,
DataListItemCells,
+ Tooltip,
} from '@patternfly/react-core';
import { Link } from 'react-router-dom';
+import { PencilAltIcon } from '@patternfly/react-icons';
+import ActionButtonCell from '@components/ActionButtonCell';
import DataListCell from '@components/DataListCell';
import DataListCheck from '@components/DataListCheck';
+import ListActionButton from '@components/ListActionButton';
import VerticalSeparator from '@components/VerticalSeparator';
import { Team } from '@types';
@@ -22,7 +27,7 @@ class TeamListItem extends React.Component {
};
render() {
- const { team, isSelected, onSelect, detailUrl } = this.props;
+ const { team, isSelected, onSelect, detailUrl, i18n } = this.props;
const labelId = `check-action-${team.id}`;
return (
@@ -48,7 +53,9 @@ class TeamListItem extends React.Component {
{team.summary_fields.organization && (
- Organization
+
+ {i18n._(t`Organization`)}
+
@@ -57,9 +64,19 @@ class TeamListItem extends React.Component {
)}
,
-
- edit button goes here
- ,
+
+ {team.summary_fields.user_capabilities.edit && (
+
+
+
+
+
+ )}
+ ,
]}
/>
diff --git a/awx/ui_next/src/screens/Team/TeamList/TeamListItem.test.jsx b/awx/ui_next/src/screens/Team/TeamList/TeamListItem.test.jsx
index b4d10fa56e..73d9329c7e 100644
--- a/awx/ui_next/src/screens/Team/TeamList/TeamListItem.test.jsx
+++ b/awx/ui_next/src/screens/Team/TeamList/TeamListItem.test.jsx
@@ -16,9 +16,8 @@ describe('', () => {
id: 1,
name: 'Team 1',
summary_fields: {
- organization: {
- id: 1,
- name: 'Default',
+ user_capabilities: {
+ edit: true,
},
},
}}
@@ -30,4 +29,50 @@ describe('', () => {
);
});
+ test('edit button shown to users with edit capabilities', () => {
+ const wrapper = mountWithContexts(
+
+
+ {}}
+ />
+
+
+ );
+ expect(wrapper.find('PencilAltIcon').exists()).toBeTruthy();
+ });
+ test('edit button hidden from users without edit capabilities', () => {
+ const wrapper = mountWithContexts(
+
+
+ {}}
+ />
+
+
+ );
+ expect(wrapper.find('PencilAltIcon').exists()).toBeFalsy();
+ });
});
diff --git a/awx/ui_next/src/screens/Team/shared/TeamForm.jsx b/awx/ui_next/src/screens/Team/shared/TeamForm.jsx
index 9259b008bb..65a3a12cbd 100644
--- a/awx/ui_next/src/screens/Team/shared/TeamForm.jsx
+++ b/awx/ui_next/src/screens/Team/shared/TeamForm.jsx
@@ -1,98 +1,91 @@
-import React, { Component } from 'react';
+import React, { useState } from 'react';
import PropTypes from 'prop-types';
-
-import { withRouter } from 'react-router-dom';
-import { Formik } from 'formik';
import { withI18n } from '@lingui/react';
import { t } from '@lingui/macro';
-
+import { Formik, Field } from 'formik';
import { Form } from '@patternfly/react-core';
-
-import FormRow from '@components/FormRow';
-import FormField from '@components/FormField';
import FormActionGroup from '@components/FormActionGroup/FormActionGroup';
+import FormField from '@components/FormField';
+import FormRow from '@components/FormRow';
+import OrganizationLookup from '@components/Lookup/OrganizationLookup';
import { required } from '@util/validators';
-class TeamForm extends Component {
- constructor(props) {
- super(props);
+function TeamForm(props) {
+ const { team, handleCancel, handleSubmit, i18n } = props;
+ const [organization, setOrganization] = useState(
+ team.summary_fields ? team.summary_fields.organization : null
+ );
- this.handleSubmit = this.handleSubmit.bind(this);
-
- this.state = {
- formIsValid: true,
- };
- }
-
- isEditingNewTeam() {
- const { team } = this.props;
- return !team.id;
- }
-
- handleSubmit(values) {
- const { handleSubmit } = this.props;
-
- handleSubmit(values);
- }
-
- render() {
- const { team, handleCancel, i18n } = this.props;
- const { formIsValid, error } = this.state;
-
- return (
- (
-
+ )}
+ />
+ );
}
-FormField.propTypes = {
- label: PropTypes.oneOfType([PropTypes.object, PropTypes.string]).isRequired,
-};
-
TeamForm.propTypes = {
- team: PropTypes.shape(),
- handleSubmit: PropTypes.func.isRequired,
handleCancel: PropTypes.func.isRequired,
+ handleSubmit: PropTypes.func.isRequired,
+ team: PropTypes.shape({}),
};
TeamForm.defaultProps = {
- team: {
- name: '',
- description: '',
- },
+ team: {},
};
-export { TeamForm as _TeamForm };
-export default withI18n()(withRouter(TeamForm));
+export default withI18n()(TeamForm);
diff --git a/awx/ui_next/src/screens/Team/shared/TeamForm.test.jsx b/awx/ui_next/src/screens/Team/shared/TeamForm.test.jsx
index 1aaca85393..0d8a483417 100644
--- a/awx/ui_next/src/screens/Team/shared/TeamForm.test.jsx
+++ b/awx/ui_next/src/screens/Team/shared/TeamForm.test.jsx
@@ -1,6 +1,6 @@
import React from 'react';
-
-import { mountWithContexts } from '@testUtils/enzymeHelpers';
+import { act } from 'react-dom/test-utils';
+import { mountWithContexts, waitForElement } from '@testUtils/enzymeHelpers';
import { sleep } from '@testUtils/testUtils';
import TeamForm from './TeamForm';
@@ -8,6 +8,7 @@ import TeamForm from './TeamForm';
jest.mock('@api');
describe('', () => {
+ let wrapper;
const meConfig = {
me: {
is_superuser: false,
@@ -17,14 +18,20 @@ describe('', () => {
id: 1,
name: 'Foo',
description: 'Bar',
+ organization: 1,
+ summary_fields: {
+ id: 1,
+ name: 'Default',
+ },
};
afterEach(() => {
+ wrapper.unmount();
jest.clearAllMocks();
});
test('changing inputs should update form values', () => {
- const wrapper = mountWithContexts(
+ wrapper = mountWithContexts(
', () => {
target: { value: 'new bar', name: 'description' },
});
expect(form.state('values').description).toEqual('new bar');
+ act(() => {
+ wrapper.find('OrganizationLookup').invoke('onBlur')();
+ wrapper.find('OrganizationLookup').invoke('onChange')({
+ id: 2,
+ name: 'Other Org',
+ });
+ });
+ expect(form.state('values').organization).toEqual(2);
});
- test('calls handleSubmit when form submitted', async () => {
+ test('should call handleSubmit when Submit button is clicked', async () => {
const handleSubmit = jest.fn();
- const wrapper = mountWithContexts(
-
- );
+ await act(async () => {
+ wrapper = mountWithContexts(
+
+ );
+ });
+ await waitForElement(wrapper, 'ContentLoading', el => el.length === 0);
expect(handleSubmit).not.toHaveBeenCalled();
wrapper.find('button[aria-label="Save"]').simulate('click');
await sleep(1);
- expect(handleSubmit).toHaveBeenCalledWith({
- name: 'Foo',
- description: 'Bar',
- });
+ expect(handleSubmit).toBeCalled();
});
- test('calls "handleCancel" when Cancel button is clicked', () => {
+ test('calls handleCancel when Cancel button is clicked', () => {
const handleCancel = jest.fn();
- const wrapper = mountWithContexts(
+ wrapper = mountWithContexts(