Merge pull request #4505 from marshmalien/awx-pf-jt-project-field

Add project single select input to job template form

Reviewed-by: https://github.com/apps/softwarefactory-project-zuul
This commit is contained in:
softwarefactory-project-zuul[bot] 2019-08-16 21:57:24 +00:00 committed by GitHub
commit de78d5d63b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 104 additions and 14 deletions

View File

@ -6,6 +6,7 @@ import Jobs from './models/Jobs';
import Labels from './models/Labels';
import Me from './models/Me';
import Organizations from './models/Organizations';
import Projects from './models/Projects';
import Root from './models/Root';
import Teams from './models/Teams';
import UnifiedJobTemplates from './models/UnifiedJobTemplates';
@ -21,6 +22,7 @@ const JobsAPI = new Jobs();
const LabelsAPI = new Labels();
const MeAPI = new Me();
const OrganizationsAPI = new Organizations();
const ProjectsAPI = new Projects();
const RootAPI = new Root();
const TeamsAPI = new Teams();
const UnifiedJobTemplatesAPI = new UnifiedJobTemplates();
@ -37,6 +39,7 @@ export {
LabelsAPI,
MeAPI,
OrganizationsAPI,
ProjectsAPI,
RootAPI,
TeamsAPI,
UnifiedJobTemplatesAPI,

View File

@ -0,0 +1,10 @@
import Base from '../Base';
class Projects extends Base {
constructor(http) {
super(http);
this.baseUrl = '/api/v2/projects/';
}
}
export default Projects;

View File

@ -57,9 +57,7 @@ describe('<JobTemplateAdd />', () => {
expect(wrapper.find('input#template-playbook').text()).toBe(
defaultProps.playbook
);
expect(wrapper.find('input#template-project').text()).toBe(
defaultProps.project
);
expect(wrapper.find('ProjectLookup').prop('value')).toBe(null);
done();
});

View File

@ -17,6 +17,7 @@ import { required } from '@util/validators';
import styled from 'styled-components';
import { JobTemplate } from '@types';
import InventoriesLookup from './InventoriesLookup';
import ProjectLookup from './ProjectLookup';
import { LabelsAPI } from '@api';
const QuestionCircleIcon = styled(PFQuestionCircleIcon)`
@ -56,6 +57,7 @@ class JobTemplateForm extends Component {
loadedLabels: [],
newLabels: [],
removedLabels: [],
project: props.template.summary_fields.project,
inventory: props.template.summary_fields.inventory,
};
this.handleNewLabel = this.handleNewLabel.bind(this);
@ -153,6 +155,7 @@ class JobTemplateForm extends Component {
contentError,
hasContentLoading,
inventory,
project,
newLabels,
removedLabels,
} = this.state;
@ -258,15 +261,21 @@ class JobTemplateForm extends Component {
/>
)}
/>
<FormField
id="template-project"
<Field
name="project"
type="number"
label={i18n._(t`Project`)}
tooltip={i18n._(t`Select the project containing the playbook
you want this job to execute.`)}
isRequired
validate={required(null, i18n)}
render={({ form }) => (
<ProjectLookup
value={project}
tooltip={i18n._(t`Select the project containing the playbook
you want this job to execute.`)}
onChange={value => {
form.setFieldValue('project', value.id);
this.setState({ project: value });
}}
required
/>
)}
/>
<FormField
id="template-playbook"

View File

@ -22,6 +22,10 @@ describe('<JobTemplateForm />', () => {
name: 'foo',
organization_id: 1,
},
project: {
id: 3,
name: 'qux',
},
labels: { results: [{ name: 'Sushi', id: 1 }, { name: 'Major', id: 2 }] },
},
};
@ -44,9 +48,13 @@ describe('<JobTemplateForm />', () => {
/>
);
await waitForElement(wrapper, 'EmptyStateBody', el => el.length === 0);
const component = wrapper.find('ChipGroup');
expect(LabelsAPI.read).toHaveBeenCalled();
expect(component.find('span#pf-random-id-1').text()).toEqual('Sushi');
expect(
wrapper
.find('FormGroup[fieldId="template-labels"] MultiSelect Chip')
.first()
.text()
).toEqual('Sushi');
done();
});
@ -77,8 +85,9 @@ describe('<JobTemplateForm />', () => {
name: 'inventory',
});
expect(form.state('values').inventory).toEqual(3);
wrapper.find('input#template-project').simulate('change', {
target: { value: 4, name: 'project' },
wrapper.find('ProjectLookup').prop('onChange')({
id: 4,
name: 'project',
});
expect(form.state('values').project).toEqual(4);
wrapper.find('input#template-playbook').simulate('change', {

View File

@ -0,0 +1,61 @@
import React from 'react';
import { string, func, bool } from 'prop-types';
import { withI18n } from '@lingui/react';
import { t } from '@lingui/macro';
import { FormGroup, Tooltip } from '@patternfly/react-core';
import { QuestionCircleIcon as PFQuestionCircleIcon } from '@patternfly/react-icons';
import { ProjectsAPI } from '@api';
import { Project } from '@types';
import Lookup from '@components/Lookup';
import styled from 'styled-components';
const QuestionCircleIcon = styled(PFQuestionCircleIcon)`
margin-left: 10px;
`;
const loadProjects = async params => ProjectsAPI.read(params);
class ProjectLookup extends React.Component {
render() {
const { value, tooltip, onChange, required, i18n } = this.props;
return (
<FormGroup
fieldId="project-lookup"
isRequired={required}
label={i18n._(t`Project`)}
>
{tooltip && (
<Tooltip position="right" content={tooltip}>
<QuestionCircleIcon />
</Tooltip>
)}
<Lookup
id="project-lookup"
lookupHeader={i18n._(t`Projects`)}
name="project"
value={value}
onLookupSave={onChange}
getItems={loadProjects}
required={required}
sortedColumnKey="name"
/>
</FormGroup>
);
}
}
ProjectLookup.propTypes = {
value: Project,
tooltip: string,
onChange: func.isRequired,
required: bool,
};
ProjectLookup.defaultProps = {
value: null,
tooltip: '',
required: false,
};
export default withI18n()(ProjectLookup);