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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -186,3 +186,8 @@ export function regExp() {
return undefined; 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, regExp,
requiredEmail, requiredEmail,
validateTime, validateTime,
protectedResourceName,
} from './validators'; } from './validators';
describe('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');
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');
});
}); });