diff --git a/awx/ui_next/src/components/LaunchButton/LaunchButton.jsx b/awx/ui_next/src/components/LaunchButton/LaunchButton.jsx
index a832a929f8..a8322d86c5 100644
--- a/awx/ui_next/src/components/LaunchButton/LaunchButton.jsx
+++ b/awx/ui_next/src/components/LaunchButton/LaunchButton.jsx
@@ -1,4 +1,4 @@
-import React, { Fragment } from 'react';
+import React, { Fragment, useState } from 'react';
import { withRouter } from 'react-router-dom';
import { number, shape } from 'prop-types';
import { withI18n } from '@lingui/react';
@@ -32,40 +32,12 @@ function canLaunchWithoutPrompt(launchData) {
);
}
-class LaunchButton extends React.Component {
- static propTypes = {
- resource: shape({
- id: number.isRequired,
- }).isRequired,
- };
-
- constructor(props) {
- super(props);
-
- this.state = {
- showLaunchPrompt: false,
- launchConfig: null,
- launchError: false,
- surveyConfig: null,
- };
-
- this.handleLaunch = this.handleLaunch.bind(this);
- this.launchWithParams = this.launchWithParams.bind(this);
- this.handleRelaunch = this.handleRelaunch.bind(this);
- this.handleLaunchErrorClose = this.handleLaunchErrorClose.bind(this);
- this.handlePromptErrorClose = this.handlePromptErrorClose.bind(this);
- }
-
- handleLaunchErrorClose() {
- this.setState({ launchError: null });
- }
-
- handlePromptErrorClose() {
- this.setState({ showLaunchPrompt: false });
- }
-
- async handleLaunch() {
- const { resource } = this.props;
+function LaunchButton({ resource, i18n, children, history }) {
+ const [showLaunchPrompt, setShowLaunchPrompt] = useState(false);
+ const [launchConfig, setLaunchConfig] = useState(null);
+ const [surveyConfig, setSurveyConfig] = useState(null);
+ const [error, setError] = useState(null);
+ const handleLaunch = async () => {
const readLaunch =
resource.type === 'workflow_job_template'
? WorkflowJobTemplatesAPI.readLaunch(resource.id)
@@ -75,33 +47,27 @@ class LaunchButton extends React.Component {
? WorkflowJobTemplatesAPI.readSurvey(resource.id)
: JobTemplatesAPI.readSurvey(resource.id);
try {
- const { data: launchConfig } = await readLaunch;
+ const { data: launch } = await readLaunch;
+ setLaunchConfig(launch);
- let surveyConfig = null;
-
- if (launchConfig.survey_enabled) {
+ if (launch.survey_enabled) {
const { data } = await readSurvey;
- surveyConfig = data;
+ setSurveyConfig(data);
}
- if (canLaunchWithoutPrompt(launchConfig)) {
- this.launchWithParams({});
+ if (canLaunchWithoutPrompt(launch)) {
+ launchWithParams({});
} else {
- this.setState({
- showLaunchPrompt: true,
- launchConfig,
- surveyConfig,
- });
+ setShowLaunchPrompt(true);
}
} catch (err) {
- this.setState({ launchError: err });
+ setError(err);
}
- }
+ };
- async launchWithParams(params) {
+ const launchWithParams = async params => {
try {
- const { history, resource } = this.props;
let jobPromise;
if (resource.type === 'job_template') {
@@ -117,13 +83,11 @@ class LaunchButton extends React.Component {
const { data: job } = await jobPromise;
history.push(`/jobs/${job.id}/output`);
} catch (launchError) {
- this.setState({ launchError });
+ setError(launchError);
}
- }
-
- async handleRelaunch() {
- const { history, resource } = this.props;
+ };
+ const handleRelaunch = async () => {
let readRelaunch;
let relaunch;
@@ -145,6 +109,7 @@ class LaunchButton extends React.Component {
try {
const { data: relaunchConfig } = await readRelaunch;
+ setLaunchConfig(relaunchConfig);
if (
!relaunchConfig.passwords_needed_to_start ||
relaunchConfig.passwords_needed_to_start.length === 0
@@ -165,53 +130,47 @@ class LaunchButton extends React.Component {
const { data: job } = await relaunch;
history.push(`/jobs/${job.id}/output`);
} else {
- this.setState({
- showLaunchPrompt: true,
- launchConfig: relaunchConfig,
- });
+ setShowLaunchPrompt(true);
}
} catch (err) {
- this.setState({ launchError: err });
+ setError(err);
}
- }
+ };
- render() {
- const {
- launchError,
- showLaunchPrompt,
- launchConfig,
- surveyConfig,
- } = this.state;
- const { resource, i18n, children } = this.props;
- return (
-
- {children({
- handleLaunch: this.handleLaunch,
- handleRelaunch: this.handleRelaunch,
- })}
- {launchError && (
-
- {i18n._(t`Failed to launch job.`)}
-
-
- )}
- {showLaunchPrompt && (
- this.setState({ showLaunchPrompt: false })}
- />
- )}
-
- );
- }
+ return (
+
+ {children({
+ handleLaunch,
+ handleRelaunch,
+ })}
+ {error && (
+ setError(null)}
+ >
+ {i18n._(t`Failed to launch job.`)}
+
+
+ )}
+ {showLaunchPrompt && (
+ setShowLaunchPrompt(false)}
+ />
+ )}
+
+ );
}
+LaunchButton.propTypes = {
+ resource: shape({
+ id: number.isRequired,
+ }).isRequired,
+};
+
export default withI18n()(withRouter(LaunchButton));
diff --git a/awx/ui_next/src/components/LaunchButton/LaunchButton.test.jsx b/awx/ui_next/src/components/LaunchButton/LaunchButton.test.jsx
index c84c7fde4d..4d4ce3ac2e 100644
--- a/awx/ui_next/src/components/LaunchButton/LaunchButton.test.jsx
+++ b/awx/ui_next/src/components/LaunchButton/LaunchButton.test.jsx
@@ -1,5 +1,6 @@
import React from 'react';
import { createMemoryHistory } from 'history';
+import { act } from 'react-dom/test-utils';
import { mountWithContexts } from '../../../testUtils/enzymeHelpers';
import { sleep } from '../../../testUtils/testUtils';
@@ -69,7 +70,7 @@ describe('LaunchButton', () => {
}
);
const button = wrapper.find('button');
- button.prop('onClick')();
+ await act(() => button.prop('onClick')());
expect(JobTemplatesAPI.readLaunch).toHaveBeenCalledWith(1);
await sleep(0);
expect(JobTemplatesAPI.launch).toHaveBeenCalledWith(1, {});
@@ -106,7 +107,7 @@ describe('LaunchButton', () => {
}
);
const button = wrapper.find('button');
- button.prop('onClick')();
+ await act(() => button.prop('onClick')());
expect(WorkflowJobTemplatesAPI.readLaunch).toHaveBeenCalledWith(1);
await sleep(0);
expect(WorkflowJobTemplatesAPI.launch).toHaveBeenCalledWith(1, {});
@@ -143,7 +144,7 @@ describe('LaunchButton', () => {
}
);
const button = wrapper.find('button');
- button.prop('onClick')();
+ await act(() => button.prop('onClick')());
expect(JobsAPI.readRelaunch).toHaveBeenCalledWith(1);
await sleep(0);
expect(JobsAPI.relaunch).toHaveBeenCalledWith(1);
@@ -180,7 +181,7 @@ describe('LaunchButton', () => {
}
);
const button = wrapper.find('button');
- button.prop('onClick')();
+ await act(() => button.prop('onClick')());
expect(WorkflowJobsAPI.readRelaunch).toHaveBeenCalledWith(1);
await sleep(0);
expect(WorkflowJobsAPI.relaunch).toHaveBeenCalledWith(1);
@@ -218,7 +219,7 @@ describe('LaunchButton', () => {
}
);
const button = wrapper.find('button');
- button.prop('onClick')();
+ await act(() => button.prop('onClick')());
expect(ProjectsAPI.readLaunchUpdate).toHaveBeenCalledWith(5);
await sleep(0);
expect(ProjectsAPI.launchUpdate).toHaveBeenCalledWith(5);
@@ -256,7 +257,7 @@ describe('LaunchButton', () => {
}
);
const button = wrapper.find('button');
- button.prop('onClick')();
+ await act(() => button.prop('onClick')());
expect(InventorySourcesAPI.readLaunchUpdate).toHaveBeenCalledWith(5);
await sleep(0);
expect(InventorySourcesAPI.launchUpdate).toHaveBeenCalledWith(5);
@@ -280,7 +281,7 @@ describe('LaunchButton', () => {
})
);
expect(wrapper.find('Modal').length).toBe(0);
- wrapper.find('button').prop('onClick')();
+ await act(() => wrapper.find('button').prop('onClick')());
await sleep(0);
wrapper.update();
expect(wrapper.find('Modal').length).toBe(1);
diff --git a/awx/ui_next/src/components/Lookup/Lookup.jsx b/awx/ui_next/src/components/Lookup/Lookup.jsx
index d6dea5e49e..197981678d 100644
--- a/awx/ui_next/src/components/Lookup/Lookup.jsx
+++ b/awx/ui_next/src/components/Lookup/Lookup.jsx
@@ -123,6 +123,7 @@ function Lookup(props) {
+
{i18n._(t`Select`)}
,
- ,
+
+ {i18n._(t`Return`)}
+ ,
+ ]}
>
- {i18n._(t`Cancel job`)}
- ,
-
- {i18n._(t`Return`)}
- ,
- ]}
- >
- {i18n._(
- t`Are you sure you want to submit the request to cancel this job?`
+ {i18n._(
+ t`Are you sure you want to submit the request to cancel this job?`
+ )}
+
)}
-
+
)}
{cancelError && (
- <>
- this.setState({ cancelError: null })}
- title={i18n._(t`Job Cancel Error`)}
- label={i18n._(t`Job Cancel Error`)}
- >
-
-
- >
+
+ {({ i18n }) => (
+ this.setState({ cancelError: null })}
+ title={i18n._(t`Job Cancel Error`)}
+ label={i18n._(t`Job Cancel Error`)}
+ >
+
+
+ )}
+
)}
{deletionError && (
- <>
- this.setState({ deletionError: null })}
- title={i18n._(t`Job Delete Error`)}
- label={i18n._(t`Job Delete Error`)}
- >
-
-
- >
+
+ {({ i18n }) => (
+ this.setState({ deletionError: null })}
+ title={i18n._(t`Job Delete Error`)}
+ label={i18n._(t`Job Delete Error`)}
+ >
+
+
+ )}
+
)}
);
@@ -731,4 +739,4 @@ class JobOutput extends Component {
}
export { JobOutput as _JobOutput };
-export default withI18n()(withRouter(JobOutput));
+export default withRouter(JobOutput);