Fixes erroneous disabling of name input field on container and instance group forms

This commit is contained in:
Alex Corey 2022-02-07 11:51:48 -05:00
parent 326d12382f
commit c40785b6eb
10 changed files with 121 additions and 45 deletions

View File

@ -30,13 +30,16 @@ function ContainerGroup({ setBreadcrumb }) {
isLoading,
error: contentError,
request: fetchInstanceGroups,
result: { instanceGroup, defaultExecution },
result: { instanceGroup, defaultControlPlane, defaultExecution },
} = useRequest(
useCallback(async () => {
const [
{ data },
{
data: { DEFAULT_EXECUTION_QUEUE_NAME },
data: {
DEFAULT_EXECUTION_QUEUE_NAME,
DEFAULT_CONTROL_PLANE_QUEUE_NAME,
},
},
] = await Promise.all([
InstanceGroupsAPI.readDetail(id),
@ -44,6 +47,7 @@ function ContainerGroup({ setBreadcrumb }) {
]);
return {
instanceGroup: data,
defaultControlPlane: DEFAULT_CONTROL_PLANE_QUEUE_NAME,
defaultExecution: DEFAULT_EXECUTION_QUEUE_NAME,
};
}, [id]),
@ -123,6 +127,7 @@ function ContainerGroup({ setBreadcrumb }) {
<Route path="/instance_groups/container_group/:id/edit">
<ContainerGroupEdit
instanceGroup={instanceGroup}
defaultControlPlane={defaultControlPlane}
defaultExecution={defaultExecution}
/>
</Route>

View File

@ -11,7 +11,7 @@ import { jsonToYaml, isJsonString } from 'util/yaml';
import ContainerGroupForm from '../shared/ContainerGroupForm';
function ContainerGroupAdd() {
function ContainerGroupAdd({ defaultExecution, defaultControlPlane }) {
const history = useHistory();
const [submitError, setSubmitError] = useState(null);
@ -93,6 +93,8 @@ function ContainerGroupAdd() {
<Card>
<CardBody>
<ContainerGroupForm
defaultControlPlane={defaultControlPlane}
defaultExecution={defaultExecution}
initialPodSpec={initialPodSpec}
onSubmit={handleSubmit}
submitError={submitError}

View File

@ -9,7 +9,11 @@ import ContentError from 'components/ContentError';
import ContentLoading from 'components/ContentLoading';
import ContainerGroupForm from '../shared/ContainerGroupForm';
function ContainerGroupEdit({ instanceGroup }) {
function ContainerGroupEdit({
instanceGroup,
defaultControlPlane,
defaultExecution,
}) {
const history = useHistory();
const [submitError, setSubmitError] = useState(null);
const detailsIUrl = `/instance_groups/container_group/${instanceGroup.id}/details`;
@ -77,6 +81,8 @@ function ContainerGroupEdit({ instanceGroup }) {
return (
<CardBody>
<ContainerGroupForm
defaultControlPlane={defaultControlPlane}
defaultExecution={defaultExecution}
instanceGroup={instanceGroup}
initialPodSpec={initialPodSpec}
onSubmit={handleSubmit}

View File

@ -6,7 +6,7 @@ import { CardBody } from 'components/Card';
import { InstanceGroupsAPI } from 'api';
import InstanceGroupForm from '../shared/InstanceGroupForm';
function InstanceGroupAdd() {
function InstanceGroupAdd({ defaultExecution, defaultControlPlane }) {
const history = useHistory();
const [submitError, setSubmitError] = useState(null);
@ -28,6 +28,8 @@ function InstanceGroupAdd() {
<Card>
<CardBody>
<InstanceGroupForm
defaultControlPlane={defaultControlPlane}
defaultExecution={defaultExecution}
onSubmit={handleSubmit}
submitError={submitError}
onCancel={handleCancel}

View File

@ -19,13 +19,21 @@ function InstanceGroups() {
request: settingsRequest,
isLoading: isSettingsRequestLoading,
error: settingsRequestError,
result: isKubernetes,
result: { isKubernetes, defaultControlPlane, defaultExecution },
} = useRequest(
useCallback(async () => {
const {
data: { IS_K8S },
data: {
IS_K8S,
DEFAULT_CONTROL_PLANE_QUEUE_NAME,
DEFAULT_EXECUTION_QUEUE_NAME,
},
} = await SettingsAPI.readCategory('all');
return IS_K8S;
return {
isKubernetes: IS_K8S,
defaultControlPlane: DEFAULT_CONTROL_PLANE_QUEUE_NAME,
defaultExecution: DEFAULT_EXECUTION_QUEUE_NAME,
};
}, []),
{ isLoading: true }
);
@ -75,16 +83,22 @@ function InstanceGroups() {
/>
<Switch>
<Route path="/instance_groups/container_group/add">
<ContainerGroupAdd />
<ContainerGroupAdd
defaultControlPlane={defaultControlPlane}
defaultExecution={defaultExecution}
/>
</Route>
<Route path="/instance_groups/container_group/:id">
<ContainerGroup setBreadcrumb={buildBreadcrumbConfig} />
</Route>
{!isSettingsRequestLoading && !isKubernetes ? (
{!isSettingsRequestLoading && !isKubernetes && (
<Route path="/instance_groups/add">
<InstanceGroupAdd />
<InstanceGroupAdd
defaultControlPlane={defaultControlPlane}
defaultExecution={defaultExecution}
/>
</Route>
) : null}
)}
<Route path="/instance_groups/:id">
<InstanceGroup setBreadcrumb={buildBreadcrumbConfig} />
</Route>

View File

@ -11,7 +11,7 @@ import FormField, {
CheckboxField,
} from 'components/FormField';
import FormActionGroup from 'components/FormActionGroup';
import { required } from 'util/validators';
import { combine, required, protectedResourceName } from 'util/validators';
import {
FormColumnLayout,
FormFullWidthLayout,
@ -21,12 +21,20 @@ import {
import CredentialLookup from 'components/Lookup/CredentialLookup';
import { VariablesField } from 'components/CodeEditor';
function ContainerGroupFormFields({ instanceGroup }) {
function ContainerGroupFormFields({
instanceGroup,
defaultControlPlane,
defaultExecution,
}) {
const { setFieldValue, setFieldTouched } = useFormikContext();
const [credentialField, credentialMeta, credentialHelpers] =
useField('credential');
const [nameField] = useField('name');
const [, { initialValue }] = useField('name');
const isProtected =
initialValue === `${defaultControlPlane}` ||
initialValue === `${defaultExecution}`;
const [overrideField] = useField('override');
@ -42,11 +50,21 @@ function ContainerGroupFormFields({ instanceGroup }) {
<>
<FormField
name="name"
helperText={
isProtected
? t`This is a protected Instance Group. The name cannot be changed.`
: ''
}
id="container-group-name"
label={t`Name`}
type="text"
validate={required(null)}
isDisabled={nameField.value === 'default'}
validate={combine([
required(null),
protectedResourceName(
t`This is a protected name for Container Groups. Please use a different name.`,
[defaultControlPlane, defaultExecution]
),
])}
isRequired
/>
<CredentialLookup

View File

@ -3,42 +3,48 @@ import { func, shape } from 'prop-types';
import { Formik, useField } from 'formik';
import { t } from '@lingui/macro';
import { Form, Tooltip } from '@patternfly/react-core';
import { Form } from '@patternfly/react-core';
import FormField, { FormSubmitError } from 'components/FormField';
import FormActionGroup from 'components/FormActionGroup';
import { required, minMaxValue } from 'util/validators';
import {
combine,
required,
protectedResourceName,
minMaxValue,
} from 'util/validators';
import { FormColumnLayout } from 'components/FormLayout';
function InstanceGroupFormFields({ defaultControlPlane, defaultExecution }) {
const [{ value }, ,] = useField('name');
const isDisabled =
value === defaultExecution || value === defaultControlPlane;
const [, { initialValue }] = useField('name');
const isProtected =
initialValue === `${defaultControlPlane}` ||
initialValue === `${defaultExecution}`;
const validators = combine([
required(null),
protectedResourceName(
[defaultControlPlane, defaultExecution],
t`This is a protected name for Instance Groups. Please use a different name.`
),
]);
return (
<>
{isDisabled ? (
<Tooltip content={t`Name cannot be changed on this Instance Group`}>
<FormField
name="name"
id="instance-group-name"
label={t`Name`}
type="text"
validate={required(null)}
isRequired
isDisabled={isDisabled}
/>
</Tooltip>
) : (
<FormField
name="name"
id="instance-group-name"
label={t`Name`}
type="text"
validate={required(null)}
isRequired
/>
)}
<FormField
name="name"
helperText={
isProtected
? t`This is a protected Instance Group. The name cannot be changed.`
: ''
}
id="instance-group-name"
label={t`Name`}
type="text"
validate={validators}
isRequired
isDisabled={isProtected}
/>
<FormField
id="instance-group-policy-instance-minimum"
label={t`Policy instance minimum`}
@ -66,7 +72,6 @@ function InstanceGroupFormFields({ defaultControlPlane, defaultExecution }) {
function InstanceGroupForm({
instanceGroup = {},
onSubmit,
onCancel,
submitError,

View File

@ -60,6 +60,7 @@ describe('<InstanceGroupForm/>', () => {
afterEach(() => {
jest.clearAllMocks();
wrapper.unmount();
});
test('Initially renders successfully', () => {

View File

@ -186,3 +186,8 @@ export function regExp() {
return undefined;
};
}
export function protectedResourceName(message, names = []) {
return (value) =>
names.some((name) => value.trim() === `${name}`) ? message : undefined;
}

View File

@ -12,6 +12,7 @@ import {
regExp,
requiredEmail,
validateTime,
protectedResourceName,
} from './validators';
describe('validators', () => {
@ -187,4 +188,21 @@ describe('validators', () => {
expect(validateTime()('12.15 PM')).toEqual('Invalid time format');
expect(validateTime()('12;15 PM')).toEqual('Invalid time format');
});
test('protectedResourceName should validate properly', () => {
expect(
protectedResourceName('failed validation', ['Alex'])('Apollo')
).toBeUndefined();
expect(
protectedResourceName('failed validation', ['Alex', 'Athena'])('alex')
).toBeUndefined();
expect(
protectedResourceName('failed validation', ['Alex', 'Athena'])('Alex')
).toEqual('failed validation');
expect(
protectedResourceName('failed validation', ['Alex'])('Alex')
).toEqual('failed validation');
expect(
protectedResourceName('failed validation', ['Alex'])('Alex ')
).toEqual('failed validation');
});
});