Clear inv src subform values when source value changes

* Test that inv file field resets when project value changes
* Remove project and inv file path from API request when type is SCM
* Update checkbox tooltip to accept node proptypes
* Format option field tooltips
This commit is contained in:
Marliana Lara 2020-05-07 08:57:08 -04:00
parent b717aabcc9
commit 4b53875a71
No known key found for this signature in database
GPG Key ID: 38C73B40DFA809EE
7 changed files with 116 additions and 27 deletions

View File

@ -1,5 +1,5 @@
import React from 'react';
import { string, func } from 'prop-types';
import { string, func, node } from 'prop-types';
import { useField } from 'formik';
import { Checkbox, Tooltip } from '@patternfly/react-core';
import { QuestionCircleIcon as PFQuestionCircleIcon } from '@patternfly/react-icons';
@ -40,7 +40,7 @@ CheckboxField.propTypes = {
name: string.isRequired,
label: string.isRequired,
validate: func,
tooltip: string,
tooltip: node,
};
CheckboxField.defaultProps = {
validate: () => {},

View File

@ -26,12 +26,21 @@ function InventorySourceAdd() {
}, [result, history]);
const handleSubmit = async form => {
const { credential, source_project, ...remainingForm } = form;
const { credential, source_path, source_project, ...remainingForm } = form;
const sourcePath = {};
const sourceProject = {};
if (form.source === 'scm') {
sourcePath.source_path =
source_path === '/ (project root)' ? '' : source_path;
sourceProject.source_project = source_project.id;
}
await request({
credential: credential?.id || null,
source_project: source_project?.id || null,
inventory: id,
...sourcePath,
...sourceProject,
...remainingForm,
});
};

View File

@ -1,5 +1,6 @@
import React, { useEffect, useCallback, useContext } from 'react';
import { Formik, useField } from 'formik';
import { Formik, useField, useFormikContext } from 'formik';
import { func, shape } from 'prop-types';
import { withI18n } from '@lingui/react';
import { t } from '@lingui/macro';
import { InventorySourcesAPI } from '@api';
@ -21,7 +22,8 @@ import { FormColumnLayout, SubFormLayout } from '@components/FormLayout';
import SCMSubForm from './InventorySourceSubForms';
const InventorySourceFormFields = ({ sourceOptions, i18n }) => {
const [sourceField, sourceMeta, sourceHelpers] = useField({
const { values, initialValues, resetForm } = useFormikContext();
const [sourceField, sourceMeta] = useField({
name: 'source',
validate: required(i18n._(t`Set a value for this field`), i18n),
});
@ -33,6 +35,18 @@ const InventorySourceFormFields = ({ sourceOptions, i18n }) => {
key: 'default',
};
const resetSubFormFields = sourceType => {
resetForm({
values: {
...initialValues,
name: values.name,
description: values.description,
custom_virtualenv: values.custom_virtualenv,
source: sourceType,
},
});
};
return (
<>
<FormField
@ -69,7 +83,7 @@ const InventorySourceFormFields = ({ sourceOptions, i18n }) => {
...sourceOptions,
]}
onChange={(event, value) => {
sourceHelpers.setValue(value);
resetSubFormFields(value);
}}
/>
</FormGroup>
@ -197,4 +211,14 @@ const InventorySourceForm = ({
);
};
InventorySourceForm.propTypes = {
onCancel: func.isRequired,
onSubmit: func.isRequired,
submitError: shape({}),
};
InventorySourceForm.defaultProps = {
submitError: null,
};
export default withI18n()(InventorySourceForm);

View File

@ -14,7 +14,7 @@ describe('<InventorySourceForm />', () => {
data: { count: 0, results: [] },
});
ProjectsAPI.readInventories.mockResolvedValue({
data: ['foo'],
data: ['foo', 'bar'],
});
InventorySourcesAPI.readOptions.mockResolvedValue({
data: {

View File

@ -31,8 +31,7 @@ const SCMSubForm = ({ i18n }) => {
} = useRequest(
useCallback(async projectId => {
const { data } = await ProjectsAPI.readInventories(projectId);
data.push('/ (project root)');
return data;
return [...data, '/ (project root)'];
}, []),
[]
);

View File

@ -3,9 +3,10 @@ import { act } from 'react-dom/test-utils';
import { mountWithContexts } from '@testUtils/enzymeHelpers';
import { Formik } from 'formik';
import SCMSubForm from './SCMSubForm';
import { ProjectsAPI } from '@api';
import { ProjectsAPI, CredentialsAPI } from '@api';
jest.mock('@api/models/Projects');
jest.mock('@api/models/Credentials');
const initialValues = {
credential: null,
@ -23,9 +24,26 @@ const initialValues = {
describe('<SCMSubForm />', () => {
let wrapper;
CredentialsAPI.read.mockResolvedValue({
data: { count: 0, results: [] },
});
ProjectsAPI.readInventories.mockResolvedValue({
data: ['foo'],
data: ['foo', 'bar'],
});
ProjectsAPI.read.mockResolvedValue({
data: {
count: 2,
results: [
{
id: 1,
name: 'mock proj one',
},
{
id: 2,
name: 'mock proj two',
},
],
},
});
beforeAll(async () => {
@ -59,10 +77,34 @@ describe('<SCMSubForm />', () => {
await act(async () => {
wrapper.find('ProjectLookup').invoke('onChange')({
id: 2,
name: 'mock proj',
name: 'mock proj two',
});
wrapper.find('ProjectLookup').invoke('onBlur')();
});
expect(ProjectsAPI.readInventories).toHaveBeenCalledWith(2);
});
test('changing source project should reset source path dropdown', async () => {
expect(wrapper.find('AnsibleSelect#source_path').prop('value')).toEqual('');
await act(async () => {
await wrapper.find('AnsibleSelect#source_path').prop('onChange')(
null,
'bar'
);
});
wrapper.update();
expect(wrapper.find('AnsibleSelect#source_path').prop('value')).toEqual(
'bar'
);
await act(async () => {
wrapper.find('ProjectLookup').invoke('onChange')({
id: 1,
name: 'mock proj one',
});
});
wrapper.update();
expect(wrapper.find('AnsibleSelect#source_path').prop('value')).toEqual('');
});
});

View File

@ -63,24 +63,39 @@ export const OptionsField = withI18n()(({ i18n }) => {
id="overwrite"
name="overwrite"
label={i18n._(t`Overwrite`)}
tooltip={i18n._(t`If checked, any hosts and groups that were
previously present on the external source but are now removed
will be removed from the Tower inventory. Hosts and groups
that were not managed by the inventory source will be promoted
to the next manually created group or if there is no manually
created group to promote them into, they will be left in the "all"
default group for the inventory. When not checked, local child
hosts and groups not found on the external source will remain
untouched by the inventory update process.`)}
tooltip={
<>
{i18n._(t`If checked, any hosts and groups that were
previously present on the external source but are now removed
will be removed from the Tower inventory. Hosts and groups
that were not managed by the inventory source will be promoted
to the next manually created group or if there is no manually
created group to promote them into, they will be left in the "all"
default group for the inventory.`)}
<br />
<br />
{i18n._(t`When not checked, local child
hosts and groups not found on the external source will remain
untouched by the inventory update process.`)}
</>
}
/>
<CheckboxField
id="overwrite_vars"
name="overwrite_vars"
label={i18n._(t`Overwrite variables`)}
tooltip={i18n._(t`If checked, all variables for child groups and hosts
will be removed and replaced by those found on the external source.
When not checked, a merge will be performed, combining local
variables with those found on the external source.`)}
tooltip={
<>
{i18n._(t`If checked, all variables for child groups
and hosts will be removed and replaced by those found
on the external source.`)}
<br />
<br />
{i18n._(t`When not checked, a merge will be performed,
combining local variables with those found on the
external source.`)}
</>
}
/>
<CheckboxField
id="update_on_launch"