mirror of
https://github.com/ansible/awx.git
synced 2026-03-18 17:37:30 -02:30
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:
@@ -6,6 +6,7 @@ import Jobs from './models/Jobs';
|
|||||||
import Labels from './models/Labels';
|
import Labels from './models/Labels';
|
||||||
import Me from './models/Me';
|
import Me from './models/Me';
|
||||||
import Organizations from './models/Organizations';
|
import Organizations from './models/Organizations';
|
||||||
|
import Projects from './models/Projects';
|
||||||
import Root from './models/Root';
|
import Root from './models/Root';
|
||||||
import Teams from './models/Teams';
|
import Teams from './models/Teams';
|
||||||
import UnifiedJobTemplates from './models/UnifiedJobTemplates';
|
import UnifiedJobTemplates from './models/UnifiedJobTemplates';
|
||||||
@@ -21,6 +22,7 @@ const JobsAPI = new Jobs();
|
|||||||
const LabelsAPI = new Labels();
|
const LabelsAPI = new Labels();
|
||||||
const MeAPI = new Me();
|
const MeAPI = new Me();
|
||||||
const OrganizationsAPI = new Organizations();
|
const OrganizationsAPI = new Organizations();
|
||||||
|
const ProjectsAPI = new Projects();
|
||||||
const RootAPI = new Root();
|
const RootAPI = new Root();
|
||||||
const TeamsAPI = new Teams();
|
const TeamsAPI = new Teams();
|
||||||
const UnifiedJobTemplatesAPI = new UnifiedJobTemplates();
|
const UnifiedJobTemplatesAPI = new UnifiedJobTemplates();
|
||||||
@@ -37,6 +39,7 @@ export {
|
|||||||
LabelsAPI,
|
LabelsAPI,
|
||||||
MeAPI,
|
MeAPI,
|
||||||
OrganizationsAPI,
|
OrganizationsAPI,
|
||||||
|
ProjectsAPI,
|
||||||
RootAPI,
|
RootAPI,
|
||||||
TeamsAPI,
|
TeamsAPI,
|
||||||
UnifiedJobTemplatesAPI,
|
UnifiedJobTemplatesAPI,
|
||||||
|
|||||||
10
awx/ui_next/src/api/models/Projects.js
Normal file
10
awx/ui_next/src/api/models/Projects.js
Normal 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;
|
||||||
@@ -57,9 +57,7 @@ describe('<JobTemplateAdd />', () => {
|
|||||||
expect(wrapper.find('input#template-playbook').text()).toBe(
|
expect(wrapper.find('input#template-playbook').text()).toBe(
|
||||||
defaultProps.playbook
|
defaultProps.playbook
|
||||||
);
|
);
|
||||||
expect(wrapper.find('input#template-project').text()).toBe(
|
expect(wrapper.find('ProjectLookup').prop('value')).toBe(null);
|
||||||
defaultProps.project
|
|
||||||
);
|
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ import { required } from '@util/validators';
|
|||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
import { JobTemplate } from '@types';
|
import { JobTemplate } from '@types';
|
||||||
import InventoriesLookup from './InventoriesLookup';
|
import InventoriesLookup from './InventoriesLookup';
|
||||||
|
import ProjectLookup from './ProjectLookup';
|
||||||
import { LabelsAPI } from '@api';
|
import { LabelsAPI } from '@api';
|
||||||
|
|
||||||
const QuestionCircleIcon = styled(PFQuestionCircleIcon)`
|
const QuestionCircleIcon = styled(PFQuestionCircleIcon)`
|
||||||
@@ -56,6 +57,7 @@ class JobTemplateForm extends Component {
|
|||||||
loadedLabels: [],
|
loadedLabels: [],
|
||||||
newLabels: [],
|
newLabels: [],
|
||||||
removedLabels: [],
|
removedLabels: [],
|
||||||
|
project: props.template.summary_fields.project,
|
||||||
inventory: props.template.summary_fields.inventory,
|
inventory: props.template.summary_fields.inventory,
|
||||||
};
|
};
|
||||||
this.handleNewLabel = this.handleNewLabel.bind(this);
|
this.handleNewLabel = this.handleNewLabel.bind(this);
|
||||||
@@ -153,6 +155,7 @@ class JobTemplateForm extends Component {
|
|||||||
contentError,
|
contentError,
|
||||||
hasContentLoading,
|
hasContentLoading,
|
||||||
inventory,
|
inventory,
|
||||||
|
project,
|
||||||
newLabels,
|
newLabels,
|
||||||
removedLabels,
|
removedLabels,
|
||||||
} = this.state;
|
} = this.state;
|
||||||
@@ -258,15 +261,21 @@ class JobTemplateForm extends Component {
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
<FormField
|
<Field
|
||||||
id="template-project"
|
|
||||||
name="project"
|
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)}
|
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
|
<FormField
|
||||||
id="template-playbook"
|
id="template-playbook"
|
||||||
|
|||||||
@@ -22,6 +22,10 @@ describe('<JobTemplateForm />', () => {
|
|||||||
name: 'foo',
|
name: 'foo',
|
||||||
organization_id: 1,
|
organization_id: 1,
|
||||||
},
|
},
|
||||||
|
project: {
|
||||||
|
id: 3,
|
||||||
|
name: 'qux',
|
||||||
|
},
|
||||||
labels: { results: [{ name: 'Sushi', id: 1 }, { name: 'Major', id: 2 }] },
|
labels: { results: [{ name: 'Sushi', id: 1 }, { name: 'Major', id: 2 }] },
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@@ -44,9 +48,13 @@ describe('<JobTemplateForm />', () => {
|
|||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
await waitForElement(wrapper, 'EmptyStateBody', el => el.length === 0);
|
await waitForElement(wrapper, 'EmptyStateBody', el => el.length === 0);
|
||||||
const component = wrapper.find('ChipGroup');
|
|
||||||
expect(LabelsAPI.read).toHaveBeenCalled();
|
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();
|
done();
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -77,8 +85,9 @@ describe('<JobTemplateForm />', () => {
|
|||||||
name: 'inventory',
|
name: 'inventory',
|
||||||
});
|
});
|
||||||
expect(form.state('values').inventory).toEqual(3);
|
expect(form.state('values').inventory).toEqual(3);
|
||||||
wrapper.find('input#template-project').simulate('change', {
|
wrapper.find('ProjectLookup').prop('onChange')({
|
||||||
target: { value: 4, name: 'project' },
|
id: 4,
|
||||||
|
name: 'project',
|
||||||
});
|
});
|
||||||
expect(form.state('values').project).toEqual(4);
|
expect(form.state('values').project).toEqual(4);
|
||||||
wrapper.find('input#template-playbook').simulate('change', {
|
wrapper.find('input#template-playbook').simulate('change', {
|
||||||
|
|||||||
61
awx/ui_next/src/screens/Template/shared/ProjectLookup.jsx
Normal file
61
awx/ui_next/src/screens/Template/shared/ProjectLookup.jsx
Normal 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);
|
||||||
Reference in New Issue
Block a user