mirror of
https://github.com/ansible/awx.git
synced 2026-04-04 17:55:06 -02:30
Merge pull request #6318 from AlexSCorey/6100-ConvertWFJTandJTtoHooks
Moves JT Form to using react hooks and custom hooks Reviewed-by: https://github.com/apps/softwarefactory-project-zuul
This commit is contained in:
@@ -1,4 +1,4 @@
|
|||||||
import React, { Component } from 'react';
|
import React, { useState, useEffect, useCallback } from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { withRouter } from 'react-router-dom';
|
import { withRouter } from 'react-router-dom';
|
||||||
import { withI18n } from '@lingui/react';
|
import { withI18n } from '@lingui/react';
|
||||||
@@ -15,6 +15,8 @@ import ContentError from '@components/ContentError';
|
|||||||
import ContentLoading from '@components/ContentLoading';
|
import ContentLoading from '@components/ContentLoading';
|
||||||
import AnsibleSelect from '@components/AnsibleSelect';
|
import AnsibleSelect from '@components/AnsibleSelect';
|
||||||
import { TagMultiSelect } from '@components/MultiSelect';
|
import { TagMultiSelect } from '@components/MultiSelect';
|
||||||
|
import useRequest from '@util/useRequest';
|
||||||
|
|
||||||
import FormActionGroup from '@components/FormActionGroup';
|
import FormActionGroup from '@components/FormActionGroup';
|
||||||
import FormField, {
|
import FormField, {
|
||||||
CheckboxField,
|
CheckboxField,
|
||||||
@@ -40,91 +42,64 @@ import { JobTemplatesAPI, ProjectsAPI } from '@api';
|
|||||||
import LabelSelect from './LabelSelect';
|
import LabelSelect from './LabelSelect';
|
||||||
import PlaybookSelect from './PlaybookSelect';
|
import PlaybookSelect from './PlaybookSelect';
|
||||||
|
|
||||||
class JobTemplateForm extends Component {
|
function JobTemplateForm({
|
||||||
static propTypes = {
|
template,
|
||||||
template: JobTemplate,
|
validateField,
|
||||||
handleCancel: PropTypes.func.isRequired,
|
handleCancel,
|
||||||
handleSubmit: PropTypes.func.isRequired,
|
handleSubmit,
|
||||||
submitError: PropTypes.shape({}),
|
handleBlur,
|
||||||
};
|
setFieldValue,
|
||||||
|
submitError,
|
||||||
static defaultProps = {
|
i18n,
|
||||||
template: {
|
touched,
|
||||||
name: '',
|
}) {
|
||||||
description: '',
|
const [contentError, setContentError] = useState(false);
|
||||||
job_type: 'run',
|
const [project, setProject] = useState(null);
|
||||||
inventory: undefined,
|
const [inventory, setInventory] = useState(
|
||||||
project: undefined,
|
template?.summary_fields?.inventory
|
||||||
playbook: '',
|
);
|
||||||
summary_fields: {
|
const [allowCallbacks, setAllowCallbacks] = useState(
|
||||||
inventory: null,
|
Boolean(template?.host_config_key)
|
||||||
labels: { results: [] },
|
|
||||||
project: null,
|
|
||||||
credentials: [],
|
|
||||||
},
|
|
||||||
isNew: true,
|
|
||||||
},
|
|
||||||
submitError: null,
|
|
||||||
};
|
|
||||||
|
|
||||||
constructor(props) {
|
|
||||||
super(props);
|
|
||||||
this.state = {
|
|
||||||
hasContentLoading: true,
|
|
||||||
contentError: false,
|
|
||||||
project: props.template.summary_fields.project,
|
|
||||||
inventory: props.template.summary_fields.inventory,
|
|
||||||
allowCallbacks: !!props.template.host_config_key,
|
|
||||||
};
|
|
||||||
this.handleProjectValidation = this.handleProjectValidation.bind(this);
|
|
||||||
this.loadRelatedInstanceGroups = this.loadRelatedInstanceGroups.bind(this);
|
|
||||||
this.handleProjectUpdate = this.handleProjectUpdate.bind(this);
|
|
||||||
this.setContentError = this.setContentError.bind(this);
|
|
||||||
this.fetchProject = this.fetchProject.bind(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
componentDidMount() {
|
|
||||||
const { validateField } = this.props;
|
|
||||||
this.setState({ contentError: null, hasContentLoading: true });
|
|
||||||
// TODO: determine when LabelSelect has finished loading labels
|
|
||||||
Promise.all([this.loadRelatedInstanceGroups(), this.fetchProject()]).then(
|
|
||||||
() => {
|
|
||||||
this.setState({ hasContentLoading: false });
|
|
||||||
validateField('project');
|
|
||||||
}
|
|
||||||
);
|
);
|
||||||
}
|
|
||||||
|
|
||||||
async fetchProject() {
|
const {
|
||||||
const { project } = this.state;
|
request: fetchProject,
|
||||||
if (project && project.id) {
|
error: projectContentError,
|
||||||
try {
|
contentLoading: hasProjectLoading,
|
||||||
const { data: projectData } = await ProjectsAPI.readDetail(project.id);
|
} = useRequest(
|
||||||
this.setState({ project: projectData });
|
useCallback(async () => {
|
||||||
} catch (err) {
|
let projectData;
|
||||||
this.setState({ contentError: err });
|
if (template?.project) {
|
||||||
|
projectData = await ProjectsAPI.readDetail(template?.project);
|
||||||
|
validateField('project');
|
||||||
|
setProject(projectData.data);
|
||||||
}
|
}
|
||||||
}
|
}, [template, validateField])
|
||||||
}
|
);
|
||||||
|
const {
|
||||||
async loadRelatedInstanceGroups() {
|
request: loadRelatedInstanceGroups,
|
||||||
const { setFieldValue, template } = this.props;
|
error: instanceGroupError,
|
||||||
if (!template.id) {
|
contentLoading: instanceGroupLoading,
|
||||||
|
} = useRequest(
|
||||||
|
useCallback(async () => {
|
||||||
|
if (!template?.id) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
try {
|
|
||||||
const { data } = await JobTemplatesAPI.readInstanceGroups(template.id);
|
const { data } = await JobTemplatesAPI.readInstanceGroups(template.id);
|
||||||
setFieldValue('initialInstanceGroups', data.results);
|
setFieldValue('initialInstanceGroups', data.results);
|
||||||
setFieldValue('instanceGroups', [...data.results]);
|
setFieldValue('instanceGroups', [...data.results]);
|
||||||
} catch (err) {
|
}, [setFieldValue, template])
|
||||||
this.setState({ contentError: err });
|
);
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
handleProjectValidation() {
|
useEffect(() => {
|
||||||
const { i18n, touched } = this.props;
|
fetchProject();
|
||||||
const { project } = this.state;
|
}, [fetchProject]);
|
||||||
return () => {
|
|
||||||
|
useEffect(() => {
|
||||||
|
loadRelatedInstanceGroups();
|
||||||
|
}, [loadRelatedInstanceGroups]);
|
||||||
|
|
||||||
|
const handleProjectValidation = () => {
|
||||||
if (!project && touched.project) {
|
if (!project && touched.project) {
|
||||||
return i18n._(t`Select a value for this field`);
|
return i18n._(t`Select a value for this field`);
|
||||||
}
|
}
|
||||||
@@ -133,37 +108,13 @@ class JobTemplateForm extends Component {
|
|||||||
}
|
}
|
||||||
return undefined;
|
return undefined;
|
||||||
};
|
};
|
||||||
}
|
|
||||||
|
|
||||||
handleProjectUpdate(project) {
|
const handleProjectUpdate = newProject => {
|
||||||
const { setFieldValue } = this.props;
|
setProject(newProject);
|
||||||
setFieldValue('project', project.id);
|
setFieldValue('project', newProject.id);
|
||||||
setFieldValue('playbook', 0);
|
setFieldValue('playbook', 0);
|
||||||
setFieldValue('scm_branch', '');
|
setFieldValue('scm_branch', '');
|
||||||
this.setState({ project });
|
};
|
||||||
}
|
|
||||||
|
|
||||||
setContentError(contentError) {
|
|
||||||
this.setState({ contentError });
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const {
|
|
||||||
contentError,
|
|
||||||
hasContentLoading,
|
|
||||||
inventory,
|
|
||||||
project,
|
|
||||||
allowCallbacks,
|
|
||||||
} = this.state;
|
|
||||||
const {
|
|
||||||
handleCancel,
|
|
||||||
handleSubmit,
|
|
||||||
handleBlur,
|
|
||||||
setFieldValue,
|
|
||||||
template,
|
|
||||||
submitError,
|
|
||||||
i18n,
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
const jobTypeOptions = [
|
const jobTypeOptions = [
|
||||||
{
|
{
|
||||||
@@ -188,17 +139,17 @@ class JobTemplateForm extends Component {
|
|||||||
{ value: '4', key: '4', label: i18n._(t`4 (Connection Debug)`) },
|
{ value: '4', key: '4', label: i18n._(t`4 (Connection Debug)`) },
|
||||||
];
|
];
|
||||||
let callbackUrl;
|
let callbackUrl;
|
||||||
if (template && template.related) {
|
if (template?.related) {
|
||||||
const { origin } = document.location;
|
const { origin } = document.location;
|
||||||
const path = template.related.callback || `${template.url}callback`;
|
const path = template.related.callback || `${template.url}callback`;
|
||||||
callbackUrl = `${origin}${path}`;
|
callbackUrl = `${origin}${path}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hasContentLoading) {
|
if (instanceGroupLoading || hasProjectLoading) {
|
||||||
return <ContentLoading />;
|
return <ContentLoading />;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (contentError) {
|
if (instanceGroupError || projectContentError) {
|
||||||
return <ContentError error={contentError} />;
|
return <ContentError error={contentError} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -274,7 +225,7 @@ class JobTemplateForm extends Component {
|
|||||||
inventory: value.id,
|
inventory: value.id,
|
||||||
organizationId: value.organization,
|
organizationId: value.organization,
|
||||||
});
|
});
|
||||||
this.setState({ inventory: value });
|
setInventory(value);
|
||||||
}}
|
}}
|
||||||
required
|
required
|
||||||
touched={form.touched.inventory}
|
touched={form.touched.inventory}
|
||||||
@@ -294,7 +245,7 @@ class JobTemplateForm extends Component {
|
|||||||
)}
|
)}
|
||||||
</Field>
|
</Field>
|
||||||
</FieldWithPrompt>
|
</FieldWithPrompt>
|
||||||
<Field name="project" validate={this.handleProjectValidation()}>
|
<Field name="project" validate={() => handleProjectValidation()}>
|
||||||
{({ form }) => (
|
{({ form }) => (
|
||||||
<ProjectLookup
|
<ProjectLookup
|
||||||
value={project}
|
value={project}
|
||||||
@@ -303,7 +254,7 @@ class JobTemplateForm extends Component {
|
|||||||
you want this job to execute.`)}
|
you want this job to execute.`)}
|
||||||
isValid={!form.touched.project || !form.errors.project}
|
isValid={!form.touched.project || !form.errors.project}
|
||||||
helperTextInvalid={form.errors.project}
|
helperTextInvalid={form.errors.project}
|
||||||
onChange={this.handleProjectUpdate}
|
onChange={handleProjectUpdate}
|
||||||
required
|
required
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
@@ -356,7 +307,7 @@ class JobTemplateForm extends Component {
|
|||||||
form={form}
|
form={form}
|
||||||
field={field}
|
field={field}
|
||||||
onBlur={() => form.setFieldTouched('playbook')}
|
onBlur={() => form.setFieldTouched('playbook')}
|
||||||
onError={this.setContentError}
|
onError={setContentError}
|
||||||
/>
|
/>
|
||||||
</FormGroup>
|
</FormGroup>
|
||||||
);
|
);
|
||||||
@@ -382,7 +333,7 @@ class JobTemplateForm extends Component {
|
|||||||
onChange={newCredentials =>
|
onChange={newCredentials =>
|
||||||
setFieldValue('credentials', newCredentials)
|
setFieldValue('credentials', newCredentials)
|
||||||
}
|
}
|
||||||
onError={this.setContentError}
|
onError={setContentError}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}}
|
}}
|
||||||
@@ -399,7 +350,7 @@ class JobTemplateForm extends Component {
|
|||||||
<LabelSelect
|
<LabelSelect
|
||||||
value={field.value}
|
value={field.value}
|
||||||
onChange={labels => setFieldValue('labels', labels)}
|
onChange={labels => setFieldValue('labels', labels)}
|
||||||
onError={this.setContentError}
|
onError={setContentError}
|
||||||
/>
|
/>
|
||||||
</FormGroup>
|
</FormGroup>
|
||||||
)}
|
)}
|
||||||
@@ -446,9 +397,7 @@ class JobTemplateForm extends Component {
|
|||||||
<TextInput
|
<TextInput
|
||||||
id="template-limit"
|
id="template-limit"
|
||||||
{...field}
|
{...field}
|
||||||
isValid={
|
isValid={!form.touched.job_type || !form.errors.job_type}
|
||||||
!form.touched.job_type || !form.errors.job_type
|
|
||||||
}
|
|
||||||
onChange={(value, event) => {
|
onChange={(value, event) => {
|
||||||
field.onChange(event);
|
field.onChange(event);
|
||||||
}}
|
}}
|
||||||
@@ -545,9 +494,7 @@ class JobTemplateForm extends Component {
|
|||||||
{({ field, form }) => (
|
{({ field, form }) => (
|
||||||
<TagMultiSelect
|
<TagMultiSelect
|
||||||
value={field.value}
|
value={field.value}
|
||||||
onChange={value =>
|
onChange={value => form.setFieldValue(field.name, value)}
|
||||||
form.setFieldValue(field.name, value)
|
|
||||||
}
|
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</Field>
|
</Field>
|
||||||
@@ -567,9 +514,7 @@ class JobTemplateForm extends Component {
|
|||||||
{({ field, form }) => (
|
{({ field, form }) => (
|
||||||
<TagMultiSelect
|
<TagMultiSelect
|
||||||
value={field.value}
|
value={field.value}
|
||||||
onChange={value =>
|
onChange={value => form.setFieldValue(field.name, value)}
|
||||||
form.setFieldValue(field.name, value)
|
|
||||||
}
|
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</Field>
|
</Field>
|
||||||
@@ -603,7 +548,7 @@ class JobTemplateForm extends Component {
|
|||||||
id="option-callbacks"
|
id="option-callbacks"
|
||||||
isChecked={allowCallbacks}
|
isChecked={allowCallbacks}
|
||||||
onChange={checked => {
|
onChange={checked => {
|
||||||
this.setState({ allowCallbacks: checked });
|
setAllowCallbacks(checked);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<CheckboxField
|
<CheckboxField
|
||||||
@@ -653,11 +598,33 @@ class JobTemplateForm extends Component {
|
|||||||
</Form>
|
</Form>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
JobTemplateForm.propTypes = {
|
||||||
|
template: JobTemplate,
|
||||||
|
handleCancel: PropTypes.func.isRequired,
|
||||||
|
handleSubmit: PropTypes.func.isRequired,
|
||||||
|
submitError: PropTypes.shape({}),
|
||||||
|
};
|
||||||
|
JobTemplateForm.defaultProps = {
|
||||||
|
template: {
|
||||||
|
name: '',
|
||||||
|
description: '',
|
||||||
|
job_type: 'run',
|
||||||
|
inventory: undefined,
|
||||||
|
project: undefined,
|
||||||
|
playbook: '',
|
||||||
|
summary_fields: {
|
||||||
|
inventory: null,
|
||||||
|
labels: { results: [] },
|
||||||
|
project: null,
|
||||||
|
credentials: [],
|
||||||
|
},
|
||||||
|
isNew: true,
|
||||||
|
},
|
||||||
|
submitError: null,
|
||||||
|
};
|
||||||
|
|
||||||
const FormikApp = withFormik({
|
const FormikApp = withFormik({
|
||||||
mapPropsToValues(props) {
|
mapPropsToValues({ template = {} }) {
|
||||||
const { template = {} } = props;
|
|
||||||
const {
|
const {
|
||||||
summary_fields = {
|
summary_fields = {
|
||||||
labels: { results: [] },
|
labels: { results: [] },
|
||||||
|
|||||||
@@ -157,6 +157,7 @@ describe('<JobTemplateForm />', () => {
|
|||||||
name: 'inventory',
|
name: 'inventory',
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
wrapper.update();
|
wrapper.update();
|
||||||
await act(async () => {
|
await act(async () => {
|
||||||
wrapper.find('input#template-scm-branch').simulate('change', {
|
wrapper.find('input#template-scm-branch').simulate('change', {
|
||||||
|
|||||||
Reference in New Issue
Block a user