Wizard: Add Hostname functionality

This adds a validated hostname input and new tests.
This commit is contained in:
regexowl 2024-12-11 09:54:16 +01:00 committed by Lucas Garfield
parent c98b7d9997
commit 5a514d1d04
7 changed files with 153 additions and 4 deletions

View file

@ -37,6 +37,7 @@ import {
useFirstBootValidation,
useDetailsValidation,
useRegistrationValidation,
useHostnameValidation,
} from './utilities/useValidation';
import {
isAwsAccountIdValid,
@ -216,6 +217,8 @@ const CreateImageWizard = ({ isEdit }: CreateImageWizardProps) => {
// Filesystem
const [filesystemPristine, setFilesystemPristine] = useState(true);
const fileSystemValidation = useFilesystemValidation();
// Hostname
const hostnameValidation = useHostnameValidation();
// Firstboot
const firstBootValidation = useFirstBootValidation();
// Details
@ -487,8 +490,12 @@ const CreateImageWizard = ({ isEdit }: CreateImageWizardProps) => {
key="wizard-hostname"
navItem={customStatusNavItem}
isHidden={!isHostnameEnabled}
status={hostnameValidation.disabledNext ? 'error' : 'default'}
footer={
<CustomWizardFooter disableNext={false} optional={true} />
<CustomWizardFooter
disableNext={hostnameValidation.disabledNext}
optional={true}
/>
}
>
<HostnameStep />

View file

@ -1,7 +1,37 @@
import React from 'react';
import { FormGroup } from '@patternfly/react-core';
import { useAppDispatch, useAppSelector } from '../../../../../store/hooks';
import {
changeHostname,
selectHostname,
} from '../../../../../store/wizardSlice';
import { useHostnameValidation } from '../../../utilities/useValidation';
import { HookValidatedInput } from '../../../ValidatedTextInput';
const HostnameInput = () => {
return <></>;
const dispatch = useAppDispatch();
const hostname = useAppSelector(selectHostname);
const stepValidation = useHostnameValidation();
const handleChange = (e: React.FormEvent, value: string) => {
dispatch(changeHostname(value));
};
return (
<FormGroup label="Hostname">
<HookValidatedInput
ariaLabel="hostname input"
value={hostname}
onChange={handleChange}
placeholder="Add a hostname"
stepValidation={stepValidation}
fieldName="hostname"
/>
</FormGroup>
);
};
export default HostnameInput;

View file

@ -80,6 +80,7 @@ import {
selectNtpServers,
selectLanguages,
selectKeyboard,
selectHostname,
} from '../../../store/wizardSlice';
import { FileSystemConfigurationType } from '../steps/FileSystem';
import {
@ -310,6 +311,7 @@ function commonRequestToState(
timezone: request.customizations.timezone?.timezone || '',
ntpservers: request.customizations.timezone?.ntpservers || [],
},
hostname: request.customizations.hostname || '',
};
}
@ -502,7 +504,7 @@ const getCustomizations = (state: RootState, orgID: string): Customizations => {
filesystem: getFileSystem(state),
users: undefined,
services: getServices(state),
hostname: undefined,
hostname: selectHostname(state) || undefined,
kernel: selectKernel(state).append
? { append: selectKernel(state).append }
: undefined,

View file

@ -18,6 +18,7 @@ import {
selectUseLatest,
selectActivationKey,
selectRegistrationType,
selectHostname,
} from '../../../store/wizardSlice';
import {
getDuplicateMountPoints,
@ -25,6 +26,7 @@ import {
isBlueprintDescriptionValid,
isMountpointMinSizeValid,
isSnapshotValid,
isHostnameValid,
} from '../validators';
export type StepValidation = {
@ -38,12 +40,14 @@ export function useIsBlueprintValid(): boolean {
const registration = useRegistrationValidation();
const filesystem = useFilesystemValidation();
const snapshot = useSnapshotValidation();
const hostname = useHostnameValidation();
const firstBoot = useFirstBootValidation();
const details = useDetailsValidation();
return (
!registration.disabledNext &&
!filesystem.disabledNext &&
!snapshot.disabledNext &&
!hostname.disabledNext &&
!firstBoot.disabledNext &&
!details.disabledNext
);
@ -133,6 +137,20 @@ export function useFirstBootValidation(): StepValidation {
};
}
export function useHostnameValidation(): StepValidation {
const hostname = useAppSelector(selectHostname);
if (!isHostnameValid(hostname)) {
return {
errors: {
hostname: 'Invalid hostname',
},
disabledNext: true,
};
}
return { errors: {}, disabledNext: false };
}
export function useDetailsValidation(): StepValidation {
const name = useAppSelector(selectBlueprintName);
const description = useAppSelector(selectBlueprintDescription);

View file

@ -90,3 +90,12 @@ export const isNtpServerValid = (ntpServer: string) => {
/^([a-z0-9-]+)?(([.:/]{1,3}[a-z0-9-]+)){1,}$/.test(ntpServer)
);
};
export const isHostnameValid = (hostname: string) => {
if (hostname !== '') {
return /^(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9])\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9-]*[A-Za-z0-9])$/.test(
hostname
);
}
return true;
};