Wizard: Refactor HookValidatedInput component
This commit splits the HookValidatedInput component into three separate functions to improve modularity and readability:
- `getValidationState`: Calculates the validation state ('default', 'success', 'error') based on whether the input is pristine and if there is an error message.
- `ValidatedInputAndTextArea`: Renders the TextInput or TextArea component, utilizing the `getValidationState` output.
- `ErrorMessage`: Displays validation error messages.
This refactoring enhances code maintainability and testability, and the updated structure is now implemented for the username field.
This commit is contained in:
parent
ba233f2c69
commit
49fa0ee735
4 changed files with 101 additions and 11 deletions
|
|
@ -1,4 +1,4 @@
|
|||
import React, { useState } from 'react';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
|
||||
import {
|
||||
HelperText,
|
||||
|
|
@ -15,7 +15,7 @@ import { EyeIcon, EyeSlashIcon } from '@patternfly/react-icons';
|
|||
|
||||
import type { StepValidation } from './utilities/useValidation';
|
||||
|
||||
interface ValidatedTextInputPropTypes extends TextInputProps {
|
||||
type ValidatedTextInputPropTypes = TextInputProps & {
|
||||
dataTestId?: string | undefined;
|
||||
ouiaId?: string;
|
||||
ariaLabel: string | undefined;
|
||||
|
|
@ -23,7 +23,7 @@ interface ValidatedTextInputPropTypes extends TextInputProps {
|
|||
validator: (value: string | undefined) => boolean;
|
||||
value: string;
|
||||
placeholder?: string;
|
||||
}
|
||||
};
|
||||
|
||||
type HookValidatedInputPropTypes = TextInputProps &
|
||||
TextAreaProps & {
|
||||
|
|
@ -38,6 +38,26 @@ type HookValidatedInputPropTypes = TextInputProps &
|
|||
inputType?: 'textInput' | 'textArea';
|
||||
};
|
||||
|
||||
type ValidationInputProp = TextInputProps &
|
||||
TextAreaProps & {
|
||||
value: string;
|
||||
placeholder: string;
|
||||
stepValidation: StepValidation;
|
||||
fieldName: string;
|
||||
inputType?: 'textInput' | 'textArea';
|
||||
ariaLabel: string;
|
||||
onChange: (
|
||||
event: React.FormEvent<HTMLInputElement | HTMLTextAreaElement>,
|
||||
value: string
|
||||
) => void;
|
||||
};
|
||||
|
||||
type ErrorMessageProps = {
|
||||
errorMessage: string;
|
||||
};
|
||||
|
||||
type ValidationResult = 'default' | 'success' | 'error';
|
||||
|
||||
export const HookPasswordValidatedInput = ({
|
||||
ariaLabel,
|
||||
placeholder,
|
||||
|
|
@ -89,6 +109,78 @@ export const HookPasswordValidatedInput = ({
|
|||
);
|
||||
};
|
||||
|
||||
export const ValidatedInputAndTextArea = ({
|
||||
value,
|
||||
stepValidation,
|
||||
fieldName,
|
||||
placeholder,
|
||||
onChange,
|
||||
ariaLabel,
|
||||
inputType = 'textInput',
|
||||
}: ValidationInputProp) => {
|
||||
const errorMessage = stepValidation.errors[fieldName];
|
||||
const hasError = errorMessage !== '';
|
||||
|
||||
const [isPristine, setIsPristine] = useState(!value);
|
||||
const validated = getValidationState(isPristine, errorMessage);
|
||||
|
||||
const handleBlur = () => {
|
||||
if (value) {
|
||||
setIsPristine(false);
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (!value) {
|
||||
setIsPristine(true);
|
||||
}
|
||||
}, [value, setIsPristine]);
|
||||
|
||||
return (
|
||||
<>
|
||||
{inputType === 'textArea' ? (
|
||||
<TextArea
|
||||
value={value}
|
||||
onChange={onChange}
|
||||
validated={validated}
|
||||
onBlur={handleBlur}
|
||||
placeholder={placeholder}
|
||||
aria-label={ariaLabel}
|
||||
/>
|
||||
) : (
|
||||
<TextInput
|
||||
value={value}
|
||||
onChange={onChange}
|
||||
validated={validated}
|
||||
onBlur={handleBlur}
|
||||
placeholder={placeholder}
|
||||
aria-label={ariaLabel}
|
||||
/>
|
||||
)}
|
||||
{hasError && <ErrorMessage errorMessage={errorMessage} />}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const getValidationState = (
|
||||
isPristine: boolean,
|
||||
errorMessage: string
|
||||
): ValidationResult => {
|
||||
const validated = isPristine ? 'default' : errorMessage ? 'error' : 'success';
|
||||
|
||||
return validated;
|
||||
};
|
||||
|
||||
export const ErrorMessage = ({ errorMessage }: ErrorMessageProps) => {
|
||||
return (
|
||||
<HelperText>
|
||||
<HelperTextItem variant="error" hasIcon>
|
||||
{errorMessage}
|
||||
</HelperTextItem>
|
||||
</HelperText>
|
||||
);
|
||||
};
|
||||
|
||||
export const HookValidatedInput = ({
|
||||
dataTestId,
|
||||
ouiaId,
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ import { useUsersValidation } from '../../../utilities/useValidation';
|
|||
import {
|
||||
HookPasswordValidatedInput,
|
||||
HookValidatedInput,
|
||||
ValidatedInputAndTextArea,
|
||||
} from '../../../ValidatedInput';
|
||||
const UserInfo = () => {
|
||||
const dispatch = useAppDispatch();
|
||||
|
|
@ -72,7 +73,7 @@ const UserInfo = () => {
|
|||
return (
|
||||
<>
|
||||
<FormGroup isRequired label="Username">
|
||||
<HookValidatedInput
|
||||
<ValidatedInputAndTextArea
|
||||
ariaLabel="blueprint user name"
|
||||
value={userName || ''}
|
||||
placeholder="Enter username"
|
||||
|
|
|
|||
|
|
@ -414,12 +414,10 @@ export function useUsersValidation(): StepValidation {
|
|||
|
||||
return {
|
||||
errors: {
|
||||
userName: !isUserNameValid(userName) ? 'Invalid user name' : '',
|
||||
userSshKey: !userSshKey
|
||||
? ''
|
||||
: !isSshKeyValid(userSshKey)
|
||||
? 'Invalid SSH key'
|
||||
: '',
|
||||
userName:
|
||||
userName && !isUserNameValid(userName) ? 'Invalid user name' : '',
|
||||
userSshKey:
|
||||
userSshKey && !isSshKeyValid(userSshKey) ? 'Invalid SSH key' : '',
|
||||
},
|
||||
disabledNext: !canProceed,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -58,7 +58,6 @@ export const isFileSystemConfigValid = (partitions: Partition[]) => {
|
|||
};
|
||||
|
||||
export const isUserNameValid = (userName: string) => {
|
||||
if (userName === undefined) return false;
|
||||
const isLengthValid = userName.length <= 32;
|
||||
const isNotNumericOnly = !/^\d+$/.test(userName);
|
||||
const isPatternValid = /^[a-zA-Z0-9][a-zA-Z0-9_.-]*[a-zA-Z0-9_$]$/.test(
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue