final fixes to tests and linting for pf v4 react upgrade

This commit is contained in:
John Mitchell 2020-06-22 14:03:44 -04:00
parent 88a38e30c3
commit 07ff3139d6
5 changed files with 10 additions and 245 deletions

View File

@ -1,103 +0,0 @@
import React, { useState } from 'react';
import PropTypes from 'prop-types';
import { withI18n } from '@lingui/react';
import { t } from '@lingui/macro';
import { useField } from 'formik';
import {
Button,
ButtonVariant,
FormGroup,
InputGroup,
Tooltip,
} from '@patternfly/react-core';
import { KeyIcon } from '@patternfly/react-icons';
import { CredentialPluginPrompt } from './CredentialPluginPrompt';
import CredentialPluginSelected from './CredentialPluginSelected';
function CredentialPluginField(props) {
const {
children,
id,
name,
label,
validate,
isRequired,
isDisabled,
i18n,
} = props;
const [showPluginWizard, setShowPluginWizard] = useState(false);
const [field, meta, helpers] = useField({ name, validate });
const isValid = !(meta.touched && meta.error);
return (
<FormGroup
fieldId={id}
helperTextInvalid={meta.error}
isRequired={isRequired}
validated={isValid ? 'default' : 'error'}
label={label}
>
{field?.value?.credential ? (
<CredentialPluginSelected
credential={field?.value?.credential}
onClearPlugin={() => helpers.setValue('')}
onEditPlugin={() => setShowPluginWizard(true)}
/>
) : (
<InputGroup>
{React.cloneElement(children, {
...field,
isRequired,
onChange: (_, event) => {
field.onChange(event);
},
})}
<Tooltip
content={i18n._(
t`Populate field from an external secret management system`
)}
>
<Button
variant={ButtonVariant.control}
aria-label={i18n._(
t`Populate field from an external secret management system`
)}
onClick={() => setShowPluginWizard(true)}
isDisabled={isDisabled}
>
<KeyIcon />
</Button>
</Tooltip>
</InputGroup>
)}
{showPluginWizard && (
<CredentialPluginPrompt
initialValues={typeof field.value === 'object' ? field.value : {}}
onClose={() => setShowPluginWizard(false)}
onSubmit={val => {
val.touched = true;
helpers.setValue(val);
setShowPluginWizard(false);
}}
/>
)}
</FormGroup>
);
}
CredentialPluginField.propTypes = {
id: PropTypes.string.isRequired,
name: PropTypes.string.isRequired,
label: PropTypes.string.isRequired,
validate: PropTypes.func,
isRequired: PropTypes.bool,
isDisabled: PropTypes.bool,
};
CredentialPluginField.defaultProps = {
validate: () => {},
isRequired: false,
isDisabled: false,
};
export default withI18n()(CredentialPluginField);

View File

@ -1,136 +0,0 @@
import React, { useState } from 'react';
import { withI18n } from '@lingui/react';
import { t } from '@lingui/macro';
import { useField } from 'formik';
import {
FileUpload,
FormGroup,
TextArea,
TextInput,
} from '@patternfly/react-core';
import {
FormColumnLayout,
FormFullWidthLayout,
} from '../../../../components/FormLayout';
import { required } from '../../../../util/validators';
import { CredentialPluginField } from '../CredentialPlugins';
const GoogleComputeEngineSubForm = ({ i18n }) => {
const [fileError, setFileError] = useState(null);
const [filename, setFilename] = useState('');
const [file, setFile] = useState('');
const inputsUsernameHelpers = useField({
name: 'inputs.username',
})[2];
const inputsProjectHelpers = useField({
name: 'inputs.project',
})[2];
const inputsSSHKeyDataHelpers = useField({
name: 'inputs.ssh_key_data',
})[2];
return (
<FormColumnLayout>
<FormGroup
fieldId="credential-gce-file"
validated={!fileError ? 'default' : 'error'}
label={i18n._(t`Service account JSON file`)}
helperText={i18n._(
t`Select a JSON formatted service account key to autopopulate the following fields.`
)}
helperTextInvalid={fileError}
>
<FileUpload
id="credential-gce-file"
value={file}
filename={filename}
filenamePlaceholder={i18n._(t`Choose a .json file`)}
onChange={async value => {
if (value) {
try {
setFile(value);
setFilename(value.name);
const fileText = await value.text();
const fileJSON = JSON.parse(fileText);
if (
!fileJSON.client_email &&
!fileJSON.project_id &&
!fileJSON.private_key
) {
setFileError(
i18n._(
t`Expected at least one of client_email, project_id or private_key to be present in the file.`
)
);
} else {
inputsUsernameHelpers.setValue(fileJSON.client_email || '');
inputsProjectHelpers.setValue(fileJSON.project_id || '');
inputsSSHKeyDataHelpers.setValue(fileJSON.private_key || '');
setFileError(null);
}
} catch {
setFileError(
i18n._(
t`There was an error parsing the file. Please check the file formatting and try again.`
)
);
}
} else {
setFile('');
setFilename('');
inputsUsernameHelpers.setValue('');
inputsProjectHelpers.setValue('');
inputsSSHKeyDataHelpers.setValue('');
setFileError(null);
}
}}
dropzoneProps={{
accept: '.json',
onDropRejected: () => {
setFileError(
i18n._(
t`File upload rejected. Please select a single .json file.`
)
);
},
}}
/>
</FormGroup>
<CredentialPluginField
id="credential-username"
label={i18n._(t`Service account email address`)}
name="inputs.username"
type="email"
validate={required(null, i18n)}
isRequired
>
<TextInput id="credential-username" />
</CredentialPluginField>
<CredentialPluginField
id="credential-project"
label={i18n._(t`Project`)}
name="inputs.project"
>
<TextInput id="credential-project" />
</CredentialPluginField>
<FormFullWidthLayout>
<CredentialPluginField
id="credential-sshKeyData"
label={i18n._(t`RSA private key`)}
name="inputs.ssh_key_data"
type="textarea"
validate={required(null, i18n)}
isRequired
>
<TextArea
id="credential-sshKeyData"
rows={6}
resizeOrientation="vertical"
/>
</CredentialPluginField>
</FormFullWidthLayout>
</FormColumnLayout>
);
};
export default withI18n()(GoogleComputeEngineSubForm);

View File

@ -178,7 +178,7 @@ describe('<InventoryGroupsList />', () => {
await waitForElement(wrapper, 'ContentError', el => el.length === 1);
});
test.skip('should show error modal when group is not successfully deleted from api', async () => {
test('should show error modal when group is not successfully deleted from api', async () => {
GroupsAPI.destroy.mockRejectedValue(
new Error({
response: {
@ -199,8 +199,8 @@ describe('<InventoryGroupsList />', () => {
});
await waitForElement(
wrapper,
'InventoryGroupsDeleteModal',
el => el.props().isModalOpen === true
'AlertModal[title="Delete Group?"]',
el => el.props().isOpen === true
);
await act(async () => {
wrapper.find('Radio[id="radio-delete"]').invoke('onChange')();
@ -211,7 +211,11 @@ describe('<InventoryGroupsList />', () => {
.find('ModalBoxFooter Button[aria-label="Delete"]')
.invoke('onClick')();
});
await waitForElement(wrapper, { title: 'Error!', variant: 'error' });
await waitForElement(
wrapper,
'AlertModal[title="Error!"] Modal',
el => el.props().isOpen === true && el.props().title === 'Error!'
);
await act(async () => {
wrapper.find('ModalBoxCloseButton').invoke('onClose')();
});

View File

@ -100,7 +100,7 @@ function HostEventModal({ onClose, hostEvent = {}, isOpen = false, i18n }) {
onClose={onClose}
title={i18n._(t`Host Details`)}
aria-label={i18n._(t`Host details modal`)}
width={'75%'}
width="75%"
>
<Tabs
aria-label={i18n._(t`Tabs`)}

View File

@ -3,7 +3,6 @@ import { t } from '@lingui/macro';
import { withI18n } from '@lingui/react';
import { CaretLeftIcon } from '@patternfly/react-icons';
import { Card, PageSection } from '@patternfly/react-core';
import RoutedTabs from '../../components/RoutedTabs';
import {
Switch,
Route,
@ -13,6 +12,7 @@ import {
useParams,
useRouteMatch,
} from 'react-router-dom';
import RoutedTabs from '../../components/RoutedTabs';
import useRequest from '../../util/useRequest';
import ContentError from '../../components/ContentError';
import JobList from '../../components/JobList';