Skip to content

Commit

Permalink
Merge pull request #7126 from vikram-raj/odc-5092
Browse files Browse the repository at this point in the history
i18n support in devconsole hpa components
  • Loading branch information
openshift-merge-robot committed Nov 11, 2020
2 parents 9e05d80 + f834cd1 commit b8b4f7a
Show file tree
Hide file tree
Showing 7 changed files with 127 additions and 78 deletions.
23 changes: 20 additions & 3 deletions frontend/packages/dev-console/locales/en/devconsole.json
Expand Up @@ -16,6 +16,26 @@
"Success Threshold must be greater than or equal to 1.": "Success Threshold must be greater than or equal to 1.",
"Path must start with /.": "Path must start with /.",
"Required": "Required",
"Note: Some fields may not be represented in this form view. Please select \"YAML view\" for full control.": "Note: Some fields may not be represented in this form view. Please select \"YAML view\" for full control.",
"Name": "Name",
"Minimum Pods": "Minimum Pods",
"Maximum Pods": "Maximum Pods",
"CPU": "CPU",
"Memory": "Memory",
"Unknown error submitting": "Unknown error submitting",
"Add": "Add",
"Resource": "Resource",
"This resource is not available": "This resource is not available",
"This is not a supported in-context type": "This is not a supported in-context type",
"Name must consist of lower-case letters, numbers and hyphens. It must start with a letter and end with a letter or number.": "Name must consist of lower-case letters, numbers and hyphens. It must start with a letter and end with a letter or number.",
"Cannot be longer than 253 characters.": "Cannot be longer than 253 characters.",
"Minimum Pods must be an integer.": "Minimum Pods must be an integer.",
"Minimum Pods must greater than or equal to 1.": "Minimum Pods must greater than or equal to 1.",
"Minimum Pods should be less than or equal to Maximum Pods.": "Minimum Pods should be less than or equal to Maximum Pods.",
"Maximum Pods must be an integer.": "Maximum Pods must be an integer.",
"Maximum Pods should be greater than or equal to Minimum Pods.": "Maximum Pods should be greater than or equal to Minimum Pods.",
"Max Pods must be defined.": "Max Pods must be defined.",
"Average Utilization must be a positive number.": "Average Utilization must be a positive number.",
"Advanced Options": "Advanced Options",
"Click on the names to access advanced options for": "Click on the names to access advanced options for",
"Routing": "Routing",
Expand All @@ -34,7 +54,6 @@
"Environment Variables (Runtime only)": "Environment Variables (Runtime only)",
"Each label is applied to each created resource.": "Each label is applied to each created resource.",
"Resource Limit": "Resource Limit",
"CPU": "CPU",
"Request": "Request",
"The minimum amount of CPU the container is guaranteed.": "The minimum amount of CPU the container is guaranteed.",
"Limit": "Limit",
Expand Down Expand Up @@ -62,7 +81,6 @@
"A unique name for the project.": "A unique name for the project.",
"Project Display Name": "Project Display Name",
"Project Description": "Project Description",
"Name": "Name",
"A unique name given to the component that will be used to name associated resources.": "A unique name given to the component that will be used to name associated resources.",
"Builder Image": "Builder Image",
"Detecting recommended builder images...": "Detecting recommended builder images...",
Expand Down Expand Up @@ -192,7 +210,6 @@
"Concurrency Limit must be lesser than or equal to {{maxSafeInteger}}.": "Concurrency Limit must be lesser than or equal to {{maxSafeInteger}}.",
"Please select a termination type.": "Please select a termination type.",
"Hostname must consist of lower-case letters, numbers, periods, and hyphens. It must start and end with a letter or number.": "Hostname must consist of lower-case letters, numbers, periods, and hyphens. It must start and end with a letter or number.",
"Cannot be longer than 253 characters.": "Cannot be longer than 253 characters.",
"Port must be an Integer.": "Port must be an Integer.",
"Port must be between 1 and 65535.": "Port must be between 1 and 65535.",
"Request must be greater than or equal to 0.": "Request must be greater than or equal to 0.",
Expand Down
@@ -1,5 +1,6 @@
import * as React from 'react';
import { useField, useFormikContext } from 'formik';
import { useTranslation } from 'react-i18next';
import { Alert, AlertActionCloseButton, Flex } from '@patternfly/react-core';
import { InputField, NumberSpinnerField } from '@console/shared';
import { HorizontalPodAutoscalerKind } from '@console/internal/module/k8s';
Expand All @@ -8,6 +9,7 @@ import { getMetricByType } from './hpa-utils';
import { HPAFormValues, SupportedMetricTypes } from './types';

const HPADetailsForm: React.FC = () => {
const { t } = useTranslation();
const name = 'formData';
const [field] = useField<HorizontalPodAutoscalerKind>(name);
const {
Expand Down Expand Up @@ -54,29 +56,39 @@ const HPADetailsForm: React.FC = () => {
<AlertActionCloseButton onClose={() => setFieldValue('showCanUseYAMLMessage', false)} />
}
isInline
title={
'Note: Some fields may not be represented in this form view. Please select "YAML view" for full control.'
}
title={t(
'devconsole~Note: Some fields may not be represented in this form view. Please select "YAML view" for full control.',
)}
variant="info"
/>
)}
<div className="row">
<div className="col-lg-8">
<Flex direction={{ default: 'column' }}>
<InputField isDisabled={nameDisabled} label="Name" name={`${name}.metadata.name`} />
<NumberSpinnerField label="Minimum Pods" name={`${name}.spec.minReplicas`} />
<NumberSpinnerField label="Maximum Pods" name={`${name}.spec.maxReplicas`} />
<InputField
isDisabled={nameDisabled}
label={t('devconsole~Name')}
name={`${name}.metadata.name`}
/>
<NumberSpinnerField
label={t('devconsole~Minimum Pods')}
name={`${name}.spec.minReplicas`}
/>
<NumberSpinnerField
label={t('devconsole~Maximum Pods')}
name={`${name}.spec.maxReplicas`}
/>
<HPAUtilizationField
disabled={cpuUtilization}
hpa={field.value}
label="CPU"
label={t('devconsole~CPU')}
onUpdate={updateField('cpu')}
type="cpu"
/>
<HPAUtilizationField
disabled={memoryUtilization}
hpa={field.value}
label="Memory"
label={t('devconsole~Memory')}
onUpdate={updateField('memory')}
type="memory"
/>
Expand Down
6 changes: 4 additions & 2 deletions frontend/packages/dev-console/src/components/hpa/HPAForm.tsx
@@ -1,6 +1,7 @@
import * as React from 'react';
import { isEmpty } from 'lodash';
import { FormikProps } from 'formik';
import { useTranslation } from 'react-i18next';
import { FlexForm, FormFooter, SyncedEditorField, YAMLEditorField } from '@console/shared';
import { EditorType } from '@console/shared/src/components/synced-editor/editor-toggle';
import { HorizontalPodAutoscalerKind, K8sResourceCommon } from '@console/internal/module/k8s';
Expand All @@ -24,6 +25,7 @@ const HPAForm: React.FC<FormikProps<HPAFormValues> & HPAFormProps> = ({
validateForm,
values,
}) => {
const { t } = useTranslation();
const isForm = values.editorType === EditorType.Form;
const formEditor = <HPADetailsForm />;
const yamlEditor = (
Expand Down Expand Up @@ -61,9 +63,9 @@ const HPAForm: React.FC<FormikProps<HPAFormValues> & HPAFormProps> = ({
handleReset={handleReset}
errorMessage={status?.submitError}
isSubmitting={isSubmitting}
submitLabel="Save"
submitLabel={t('devconsole~Save')}
disableSubmit={isForm && !isEmpty(errors)}
resetLabel="Cancel"
resetLabel={t('devconsole~Cancel')}
sticky
/>
</FlexForm>
Expand Down
@@ -1,5 +1,6 @@
import * as React from 'react';
import { Formik, FormikHelpers, FormikProps } from 'formik';
import { useTranslation } from 'react-i18next';
import { EditorType } from '@console/shared/src/components/synced-editor/editor-toggle';
import { safeYAMLToJS } from '@console/shared/src/utils/yaml';
import { HorizontalPodAutoscalerModel } from '@console/internal/models';
Expand Down Expand Up @@ -29,6 +30,7 @@ type HPAFormikFormProps = {
};

const HPAFormikForm: React.FC<HPAFormikFormProps> = ({ existingHPA, targetResource }) => {
const { t } = useTranslation();
const initialValues: HPAFormValues = {
showCanUseYAMLMessage: true,
disabledFields: {
Expand Down Expand Up @@ -62,7 +64,9 @@ const HPAFormikForm: React.FC<HPAFormikFormProps> = ({ existingHPA, targetResour
})
.catch((error) => {
helpers.setSubmitting(false);
helpers.setStatus({ submitError: error?.message || 'Unknown error submitting' });
helpers.setStatus({
submitError: error?.message || t('devconsole~Unknown error submitting'),
});
});
};

Expand All @@ -71,7 +75,7 @@ const HPAFormikForm: React.FC<HPAFormikFormProps> = ({ existingHPA, targetResour
initialValues={initialValues}
onSubmit={handleSubmit}
onReset={history.goBack}
validationSchema={hpaValidationSchema}
validationSchema={hpaValidationSchema(t)}
>
{(props: FormikProps<HPAFormValues>) => (
<HPAForm {...props} targetResource={targetResource} />
Expand Down
6 changes: 5 additions & 1 deletion frontend/packages/dev-console/src/components/hpa/HPAPage.tsx
@@ -1,6 +1,7 @@
import * as React from 'react';
import { Helmet } from 'react-helmet';
import { PageBody } from '@console/shared';
import { useTranslation } from 'react-i18next';
import { ErrorPage404 } from '@console/internal/components/error';
import { LoadingBox, LoadingInline, PageComponentProps } from '@console/internal/components/utils';
import { useK8sWatchResource } from '@console/internal/components/utils/k8s-watch-hook';
Expand All @@ -13,6 +14,7 @@ import { getLimitWarning, VALID_HPA_TARGET_KINDS } from './hpa-utils';
import { useRelatedHPA } from './hooks';

const HPAPage: React.FC<PageComponentProps> = (props) => {
const { t } = useTranslation();
const {
match: {
params: { ns, resourceRef, name },
Expand All @@ -35,7 +37,9 @@ const HPAPage: React.FC<PageComponentProps> = (props) => {
const error = hpaError || workloadError?.message;

const validSupportedType = VALID_HPA_TARGET_KINDS.includes(kind);
const title = `${hpa ? 'Edit' : 'Add'} ${HorizontalPodAutoscalerModel.label}`;
const title = `${hpa ? t('devconsole~Edit') : t('devconsole~Add')} ${
HorizontalPodAutoscalerModel.label
}`;

if (!breakdown) {
return <ErrorPage404 />;
Expand Down
13 changes: 10 additions & 3 deletions frontend/packages/dev-console/src/components/hpa/HPAPageHeader.tsx
@@ -1,4 +1,5 @@
import * as React from 'react';
import { useTranslation } from 'react-i18next';
import { Alert, Flex } from '@patternfly/react-core';
import { ResourceLink } from '@console/internal/components/utils';

Expand All @@ -19,24 +20,30 @@ const HPAPageHeader: React.FC<HPAPageHeaderProps> = ({
limitWarning,
validSupportedType,
}) => {
const { t } = useTranslation();
return (
<Flex direction={{ default: 'column' }}>
<h1 className="pf-c-title pf-m-2xl">{title}</h1>
{validSupportedType ? (
<>
<div>
Resource <ResourceLink inline linkTo={false} kind={kind} name={name} />
{t('devconsole~Resource')}{' '}
<ResourceLink inline linkTo={false} kind={kind} name={name} />
</div>
{loadError ? (
<Alert isInline variant="danger" title="This resource is not available">
<Alert isInline variant="danger" title={t('devconsole~This resource is not available')}>
{loadError}
</Alert>
) : (
limitWarning && <Alert isInline variant="warning" title={limitWarning} />
)}
</>
) : (
<Alert isInline variant="danger" title="This is not a supported in-context type" />
<Alert
isInline
variant="danger"
title={t('devconsole~This is not a supported in-context type')}
/>
)}
</Flex>
);
Expand Down
121 changes: 62 additions & 59 deletions frontend/packages/dev-console/src/components/hpa/validation-utils.ts
@@ -1,66 +1,69 @@
import * as yup from 'yup';
import { TFunction } from 'i18next';
import { EditorType } from '@console/shared/src/components/synced-editor/editor-toggle';

export const hpaValidationSchema = yup.object({
editorType: yup.string(),
formData: yup.object().when('editorType', {
is: EditorType.Form,
then: yup.object({
metadata: yup.object({
name: yup
.string()
.matches(/^([a-z]([-a-z0-9]*[a-z0-9])?)*$/, {
message:
'Name must consist of lower-case letters, numbers and hyphens. It must start with a letter and end with a letter or number.',
excludeEmptyString: true,
})
.max(253, 'Cannot be longer than 253 characters.')
.required('Required'),
}),
spec: yup.object({
minReplicas: yup
.number()
.integer('Minimum Pods must be an integer.')
.min(1, 'Minimum Pods must greater than or equal to 1.')
.test(
'test-less-than-max',
'Minimum Pods should be less than or equal to Maximum Pods.',
function(minReplicas) {
const { maxReplicas } = this.parent;
return minReplicas <= maxReplicas;
},
),
maxReplicas: yup
.number()
.integer('Maximum Pods must be an integer.')
.test(
'test-greater-than-min',
'Maximum Pods should be greater than or equal to Minimum Pods.',
function(maxReplicas) {
const { minReplicas } = this.parent;
return minReplicas <= maxReplicas;
},
)
.required('Max Pods must be defined.'),
metrics: yup.array(
yup.object({
resource: yup.object({
target: yup.object({
averageUtilization: yup
.mixed()
.test(
'test-for-valid-utilization',
'Average Utilization must be a positive number.',
function(avgUtilization) {
if (!avgUtilization) return true;
return /^\d+$/.test(String(avgUtilization));
},
),
export const hpaValidationSchema = (t: TFunction) =>
yup.object({
editorType: yup.string(),
formData: yup.object().when('editorType', {
is: EditorType.Form,
then: yup.object({
metadata: yup.object({
name: yup
.string()
.matches(/^([a-z]([-a-z0-9]*[a-z0-9])?)*$/, {
message: t(
'devconsole~Name must consist of lower-case letters, numbers and hyphens. It must start with a letter and end with a letter or number.',
),
excludeEmptyString: true,
})
.max(253, t('devconsole~Cannot be longer than 253 characters.'))
.required(t('devconsole~Required')),
}),
spec: yup.object({
minReplicas: yup
.number()
.integer(t('devconsole~Minimum Pods must be an integer.'))
.min(1, t('devconsole~Minimum Pods must greater than or equal to 1.'))
.test(
'test-less-than-max',
t('devconsole~Minimum Pods should be less than or equal to Maximum Pods.'),
function(minReplicas) {
const { maxReplicas } = this.parent;
return minReplicas <= maxReplicas;
},
),
maxReplicas: yup
.number()
.integer(t('devconsole~Maximum Pods must be an integer.'))
.test(
'test-greater-than-min',
t('devconsole~Maximum Pods should be greater than or equal to Minimum Pods.'),
function(maxReplicas) {
const { minReplicas } = this.parent;
return minReplicas <= maxReplicas;
},
)
.required(t('devconsole~Max Pods must be defined.')),
metrics: yup.array(
yup.object({
resource: yup.object({
target: yup.object({
averageUtilization: yup
.mixed()
.test(
'test-for-valid-utilization',
t('devconsole~Average Utilization must be a positive number.'),
function(avgUtilization) {
if (!avgUtilization) return true;
return /^\d+$/.test(String(avgUtilization));
},
),
}),
}),
}),
}),
),
),
}),
}),
}),
}),
});
});

0 comments on commit b8b4f7a

Please sign in to comment.