diff --git a/frontend/packages/console-shared/src/components/formik-fields/InputField.tsx b/frontend/packages/console-shared/src/components/formik-fields/InputField.tsx index 9e70ba87b55..158b1e79dfd 100644 --- a/frontend/packages/console-shared/src/components/formik-fields/InputField.tsx +++ b/frontend/packages/console-shared/src/components/formik-fields/InputField.tsx @@ -1,6 +1,6 @@ import * as React from 'react'; import { useField } from 'formik'; -import { FormGroup, TextInput } from '@patternfly/react-core'; +import { FormGroup, TextInput, ValidatedOptions } from '@patternfly/react-core'; import { InputFieldProps } from './field-types'; import { getFieldId } from './field-utils'; @@ -9,6 +9,8 @@ const InputField: React.FC = ({ helpText, required, onChange, + validated, + helpTextInvalid, ...props }) => { const [field, { touched, error }] = useField(props.name); @@ -20,16 +22,16 @@ const InputField: React.FC = ({ fieldId={fieldId} label={label} helperText={helpText} - helperTextInvalid={errorMessage} - isValid={isValid} + helperTextInvalid={errorMessage || helpTextInvalid} + validated={!isValid ? ValidatedOptions.error : validated} isRequired={required} > { diff --git a/frontend/packages/console-shared/src/components/formik-fields/field-types.ts b/frontend/packages/console-shared/src/components/formik-fields/field-types.ts index dd7d6f9f73b..15ec402e75b 100644 --- a/frontend/packages/console-shared/src/components/formik-fields/field-types.ts +++ b/frontend/packages/console-shared/src/components/formik-fields/field-types.ts @@ -1,9 +1,10 @@ -import { TextInputTypes } from '@patternfly/react-core'; +import { TextInputTypes, ValidatedOptions } from '@patternfly/react-core'; export interface FieldProps { name: string; label?: string; helpText?: React.ReactNode; + helpTextInvalid?: React.ReactNode; required?: boolean; style?: React.CSSProperties; isReadOnly?: boolean; @@ -11,6 +12,7 @@ export interface FieldProps { disableAddRow?: boolean; className?: string; isDisabled?: boolean; + validated?: ValidatedOptions; } export interface InputFieldProps extends FieldProps { diff --git a/frontend/packages/dev-console/src/components/edit-application/__tests__/edit-application-data.ts b/frontend/packages/dev-console/src/components/edit-application/__tests__/edit-application-data.ts index f0f35b35bc7..a905b5d6cce 100644 --- a/frontend/packages/dev-console/src/components/edit-application/__tests__/edit-application-data.ts +++ b/frontend/packages/dev-console/src/components/edit-application/__tests__/edit-application-data.ts @@ -1,7 +1,8 @@ +import { ValidatedOptions } from '@patternfly/react-core'; import { K8sResourceKind } from '@console/internal/module/k8s'; import { ServiceModel } from '@console/knative-plugin'; import { AppResources } from '../edit-application-types'; -import { GitImportFormData, Resources, DeployImageFormData } from '../../import/import-types'; +import { DeployImageFormData, GitImportFormData, Resources } from '../../import/import-types'; import { UNASSIGNED_KEY } from '../../import/app/ApplicationSelector'; export const knativeService: K8sResourceKind = { @@ -416,7 +417,7 @@ export const gitImportInitialValues: GitImportFormData = { dir: '/', showGitType: false, secret: '', - isUrlValidated: false, + urlValidation: ValidatedOptions.default, isUrlValidating: false, }, docker: { dockerfilePath: 'Dockerfile', containerPort: 8080 }, diff --git a/frontend/packages/dev-console/src/components/edit-application/edit-application-utils.ts b/frontend/packages/dev-console/src/components/edit-application/edit-application-utils.ts index 6f532386ff6..640ac8c4536 100644 --- a/frontend/packages/dev-console/src/components/edit-application/edit-application-utils.ts +++ b/frontend/packages/dev-console/src/components/edit-application/edit-application-utils.ts @@ -1,4 +1,5 @@ import * as _ from 'lodash'; +import { ValidatedOptions } from '@patternfly/react-core'; import { K8sResourceKind, referenceFor, referenceForModel } from '@console/internal/module/k8s'; import { BuildStrategyType } from '@console/internal/components/build'; import { DeploymentConfigModel, DeploymentModel } from '@console/internal/models'; @@ -51,7 +52,7 @@ export const getGitData = (buildConfig: K8sResourceKind) => { dir: _.get(buildConfig, 'spec.source.contextDir', ''), showGitType: false, secret: _.get(buildConfig, 'spec.source.sourceSecret.name', ''), - isUrlValidated: false, + urlValidation: ValidatedOptions.default, isUrlValidating: false, }; return gitData; diff --git a/frontend/packages/dev-console/src/components/import/ImportForm.tsx b/frontend/packages/dev-console/src/components/import/ImportForm.tsx index 39f54963f92..ab7dba31d7c 100644 --- a/frontend/packages/dev-console/src/components/import/ImportForm.tsx +++ b/frontend/packages/dev-console/src/components/import/ImportForm.tsx @@ -1,6 +1,7 @@ import * as React from 'react'; import { Formik } from 'formik'; import * as _ from 'lodash'; +import { ValidatedOptions } from '@patternfly/react-core'; import { history, AsyncComponent } from '@console/internal/components/utils'; import { getActivePerspective, getActiveApplication } from '@console/internal/reducers/ui'; import { RootState } from '@console/internal/redux'; @@ -59,7 +60,7 @@ const ImportForm: React.FC = ({ dir: '/', showGitType: false, secret: '', - isUrlValidated: false, + urlValidation: ValidatedOptions.default, isUrlValidating: false, }, docker: { diff --git a/frontend/packages/dev-console/src/components/import/__mocks__/import-validation-mock.ts b/frontend/packages/dev-console/src/components/import/__mocks__/import-validation-mock.ts index d8afb15ace4..1b892c77115 100644 --- a/frontend/packages/dev-console/src/components/import/__mocks__/import-validation-mock.ts +++ b/frontend/packages/dev-console/src/components/import/__mocks__/import-validation-mock.ts @@ -1,3 +1,4 @@ +import { ValidatedOptions } from '@patternfly/react-core'; import { GitImportFormData, Resources } from '../import-types'; export const mockFormData: GitImportFormData = { @@ -19,7 +20,7 @@ export const mockFormData: GitImportFormData = { dir: '', showGitType: false, secret: '', - isUrlValidated: false, + urlValidation: ValidatedOptions.default, isUrlValidating: false, }, docker: { diff --git a/frontend/packages/dev-console/src/components/import/__tests__/import-submit-utils-data.ts b/frontend/packages/dev-console/src/components/import/__tests__/import-submit-utils-data.ts index ba6b6f93758..c70c3d80a6e 100644 --- a/frontend/packages/dev-console/src/components/import/__tests__/import-submit-utils-data.ts +++ b/frontend/packages/dev-console/src/components/import/__tests__/import-submit-utils-data.ts @@ -1,3 +1,4 @@ +import { ValidatedOptions } from '@patternfly/react-core'; import { GitImportFormData, Resources } from '../import-types'; export const mockPipelineTemplate = { @@ -164,7 +165,7 @@ export const defaultData: GitImportFormData = { dir: '/', showGitType: false, secret: '', - isUrlValidated: true, + urlValidation: ValidatedOptions.default, isUrlValidating: false, }, docker: { diff --git a/frontend/packages/dev-console/src/components/import/git/GitSection.tsx b/frontend/packages/dev-console/src/components/import/git/GitSection.tsx index c30fa352fa8..92ebe57c814 100644 --- a/frontend/packages/dev-console/src/components/import/git/GitSection.tsx +++ b/frontend/packages/dev-console/src/components/import/git/GitSection.tsx @@ -1,9 +1,7 @@ import * as React from 'react'; import { useFormikContext, FormikValues, useField } from 'formik'; -import { Alert, TextInputTypes } from '@patternfly/react-core'; +import { Alert, TextInputTypes, ValidatedOptions } from '@patternfly/react-core'; import { getGitService, GitProvider } from '@console/git-service'; -import { LoadingInline } from '@console/internal/components/utils'; -import { CheckCircleIcon } from '@patternfly/react-icons'; import { InputField, DropdownField, useFormikValidationFix } from '@console/shared'; import { GitReadableTypes, GitTypes } from '../import-types'; import { detectGitType, detectGitRepoName } from '../import-validation-utils'; @@ -17,9 +15,7 @@ export interface GitSectionProps { } const GitSection: React.FC = ({ showSample }) => { - const { values, setFieldValue, setFieldTouched, setFieldError } = useFormikContext< - FormikValues - >(); + const { values, setFieldValue, setFieldTouched } = useFormikContext(); const [, { touched: gitTypeTouched }] = useField('git.type'); const tag = values.image.tagObj; const sampleRepo = showSample && getSampleRepo(tag); @@ -46,7 +42,7 @@ const GitSection: React.FC = ({ showSample }) => { const isReachable = gitService && (await gitService.isRepoReachable()); setFieldValue('git.isUrlValidating', false); if (isReachable) { - setFieldValue('git.isUrlValidated', true); + setFieldValue('git.urlValidation', ValidatedOptions.success); setFieldValue('image.isRecommending', true); const buildTools = await gitService.detectBuildTypes(); setFieldValue('image.isRecommending', false); @@ -61,20 +57,12 @@ const GitSection: React.FC = ({ showSample }) => { } else { setFieldValue('image.recommended', ''); setFieldValue('image.couldNotRecommend', false); - setFieldValue('git.isUrlValidated', false); - setFieldError('git.url', 'Git repository is not reachable.'); + setFieldValue('git.urlValidation', ValidatedOptions.error); } - }, [ - setFieldError, - setFieldTouched, - setFieldValue, - values.application.name, - values.git, - values.name, - ]); + }, [setFieldTouched, setFieldValue, values.application.name, values.git, values.name]); const handleGitUrlChange: React.ReactEventHandler = React.useCallback(() => { - setFieldValue('git.isUrlValidated', false); + setFieldValue('git.urlValidation', ValidatedOptions.default); values.image.recommended && setFieldValue('image.recommended', ''); values.image.couldNotRecommend && setFieldValue('image.couldNotRecommend', false); }, [setFieldValue, values.image.couldNotRecommend, values.image.recommended]); @@ -104,18 +92,10 @@ const GitSection: React.FC = ({ showSample }) => { const getHelpText = () => { if (values.git.isUrlValidating) { - return ( - - Validating... - - ); + return 'Validating...'; } - if (values.git.isUrlValidated) { - return ( - - Validated - - ); + if (values.git.urlValidation === ValidatedOptions.success) { + return 'Validated'; } return ''; }; @@ -129,8 +109,11 @@ const GitSection: React.FC = ({ showSample }) => { name="git.url" label="Git Repo URL" helpText={getHelpText()} + helpTextInvalid="Git repository is not reachable." + validated={values.git.urlValidation} onChange={handleGitUrlChange} onBlur={handleGitUrlBlur} + data-test-id="git-form-input-url" required /> {values.git.showGitType && ( diff --git a/frontend/packages/dev-console/src/components/import/import-types.ts b/frontend/packages/dev-console/src/components/import/import-types.ts index 145382079c2..b01bfde5dce 100644 --- a/frontend/packages/dev-console/src/components/import/import-types.ts +++ b/frontend/packages/dev-console/src/components/import/import-types.ts @@ -1,3 +1,4 @@ +import { ValidatedOptions } from '@patternfly/react-core'; import { K8sResourceKind, ContainerPort } from '@console/internal/module/k8s'; import { LazyLoader } from '@console/plugin-sdk'; import { NameValuePair, NameValueFromPair } from '@console/shared'; @@ -133,7 +134,7 @@ export interface GitData { dir: string; showGitType: boolean; secret: string; - isUrlValidated: boolean; + urlValidation: ValidatedOptions; isUrlValidating: boolean; } diff --git a/frontend/public/style/_overrides.scss b/frontend/public/style/_overrides.scss index 48663120764..7963379f00c 100644 --- a/frontend/public/style/_overrides.scss +++ b/frontend/public/style/_overrides.scss @@ -252,6 +252,17 @@ h6 { } } +@-webkit-keyframes autofill-success { + to { + background: var(--pf-c-form-control--success--Background); + } +} +@-webkit-keyframes autofill-invalid { + to { + background: var(--pf-c-form-control--invalid--Background); + } +} + // specificity targeting form elements to override --pf-global--FontSize--md .pf-c-page, .modal-dialog { @@ -267,6 +278,20 @@ h6 { .pf-l-stack { font-size: $font-size-base; } + .pf-c-form-control.pf-m-success, .pf-c-form-control[aria-invalid="true"] { + --pf-global--FontSize--md: #{$font-size-base}; + } + .pf-c-form-control { + &:-webkit-autofill { + -webkit-animation-fill-mode: both; + &.pf-m-success { + -webkit-animation-name: autofill-success; + } + &[aria-invalid="true"] { + -webkit-animation-name: autofill-invalid; + } + } + } } .pf-c-page__sidebar {