Wizard: Skip directly to Review

When clicking "Review and finish" the user skips directly to the Review step and the name of the blueprint gets automatically populated.
This commit is contained in:
regexowl 2025-01-06 14:08:41 +01:00 committed by Lucas Garfield
parent 4af4b56332
commit d77ac55482
12 changed files with 64 additions and 46 deletions

View file

@ -114,7 +114,7 @@ export const CustomWizardFooter = ({
ouiaId="wizard-review-and-finish-btn"
variant="tertiary"
onClick={() => {
if (!beforeNext || beforeNext()) goToStepById('step-details');
if (!beforeNext || beforeNext()) goToStepById('step-review');
}}
isDisabled={disableNext}
>

View file

@ -1,4 +1,4 @@
import React, { useEffect } from 'react';
import React from 'react';
import {
Form,
@ -14,12 +14,10 @@ import { useAppDispatch, useAppSelector } from '../../../../store/hooks';
import {
changeBlueprintDescription,
changeBlueprintName,
selectArchitecture,
selectBlueprintDescription,
selectBlueprintName,
selectDistribution,
} from '../../../../store/wizardSlice';
import { generateDefaultName } from '../../utilities/generateDefaultName';
import { useGenerateDefaultName } from '../../utilities/useGenerateDefaultName';
import { useDetailsValidation } from '../../utilities/useValidation';
import { HookValidatedInput } from '../../ValidatedTextInput';
@ -27,17 +25,9 @@ const DetailsStep = () => {
const dispatch = useAppDispatch();
const blueprintName = useAppSelector(selectBlueprintName);
const blueprintDescription = useAppSelector(selectBlueprintDescription);
const distribution = useAppSelector(selectDistribution);
const arch = useAppSelector(selectArchitecture);
useEffect(() => {
if (!blueprintName) {
dispatch(changeBlueprintName(generateDefaultName(distribution, arch)));
}
// This useEffect hook should run *only* on mount and therefore has an empty
// dependency array. eslint's exhaustive-deps rule does not support this use.
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
useGenerateDefaultName();
const handleNameChange = (
_event: React.FormEvent<HTMLInputElement>,
name: string

View file

@ -9,6 +9,7 @@ import {
selectBlueprintDescription,
selectBlueprintName,
} from '../../../../store/wizardSlice';
import { useGenerateDefaultName } from '../../utilities/useGenerateDefaultName';
const ReviewStep = ({
snapshottingEnabled,
@ -18,6 +19,8 @@ const ReviewStep = ({
const blueprintName = useAppSelector(selectBlueprintName);
const blueprintDescription = useAppSelector(selectBlueprintDescription);
useGenerateDefaultName();
return (
<Form>
<Title headingLevel="h1" size="xl">

View file

@ -1,17 +0,0 @@
import { Distributions, ImageRequest } from '../../../store/imageBuilderApi';
export const generateDefaultName = (
distribution: Distributions,
arch: ImageRequest['architecture']
) => {
const date = new Date();
const day = date.getDate().toString().padStart(2, '0');
const month = (date.getMonth() + 1).toString().padStart(2, '0');
const year = date.getFullYear().toString();
const hours = date.getHours().toString().padStart(2, '0');
const minutes = date.getMinutes().toString().padStart(2, '0');
const dateTimeString = `${month}${day}${year}-${hours}${minutes}`;
return `${distribution}-${arch}-${dateTimeString}`;
};

View file

@ -0,0 +1,42 @@
import { useEffect } from 'react';
import { useAppDispatch, useAppSelector } from '../../../store/hooks';
import { Distributions, ImageRequest } from '../../../store/imageBuilderApi';
import {
changeBlueprintName,
selectArchitecture,
selectBlueprintName,
selectDistribution,
} from '../../../store/wizardSlice';
const generateDefaultName = (
distribution: Distributions,
arch: ImageRequest['architecture']
) => {
const date = new Date();
const day = date.getDate().toString().padStart(2, '0');
const month = (date.getMonth() + 1).toString().padStart(2, '0');
const year = date.getFullYear().toString();
const hours = date.getHours().toString().padStart(2, '0');
const minutes = date.getMinutes().toString().padStart(2, '0');
const dateTimeString = `${month}${day}${year}-${hours}${minutes}`;
return `${distribution}-${arch}-${dateTimeString}`;
};
export const useGenerateDefaultName = () => {
const dispatch = useAppDispatch();
const blueprintName = useAppSelector(selectBlueprintName);
const distribution = useAppSelector(selectDistribution);
const arch = useAppSelector(selectArchitecture);
useEffect(() => {
if (!blueprintName) {
dispatch(changeBlueprintName(generateDefaultName(distribution, arch)));
}
// This useEffect hook should run *only* on mount and therefore has an empty
// dependency array. eslint's exhaustive-deps rule does not support this use.
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
};

View file

@ -140,13 +140,13 @@ describe('Step File system configuration', () => {
const user = userEvent.setup();
test('clicking Review and finish leads to Details', async () => {
test('clicking Review and finish leads to Review', async () => {
await renderCreateMode();
await selectGuestImage();
await goToFileSystemConfigurationStep();
await clickReviewAndFinish();
await screen.findByRole('heading', {
name: 'Details',
name: /Review/i,
});
});

View file

@ -123,12 +123,12 @@ describe('First Boot step', () => {
await screen.findByText('First boot configuration');
});
test('clicking Review and finish leads to Details', async () => {
test('clicking Review and finish leads to Review', async () => {
await renderCreateMode();
await goToFirstBootStep();
await clickReviewAndFinish();
await screen.findByRole('heading', {
name: 'Details',
name: /Review/i,
});
});

View file

@ -216,13 +216,13 @@ describe('Step OpenSCAP', () => {
});
});
test('clicking Review and finish leads to Details', async () => {
test('clicking Review and finish leads to Review', async () => {
await renderCreateMode();
await selectGuestImageTarget();
await goToOscapStep();
await clickReviewAndFinish();
await screen.findByRole('heading', {
name: 'Details',
name: /Review/i,
});
});
});

View file

@ -202,12 +202,12 @@ describe('Step Packages', () => {
await verifyCancelButton(router);
});
test('clicking Review and finish leads to Details', async () => {
test('clicking Review and finish leads to Review', async () => {
await renderCreateMode();
await goToPackagesStep();
await clickReviewAndFinish();
await screen.findByRole('heading', {
name: 'Details',
name: /Review/i,
});
});

View file

@ -144,12 +144,12 @@ describe('Step Registration', () => {
await verifyCancelButton(router);
});
test('clicking Review and finish leads to Details', async () => {
test('clicking Review and finish leads to Review', async () => {
await renderCreateMode();
await goToRegistrationStep();
await clickReviewAndFinish();
await screen.findByRole('heading', {
name: 'Details',
name: /Review/i,
});
});

View file

@ -224,12 +224,12 @@ describe('Step Custom repositories', () => {
await waitFor(() => expect(firstRepoCheckbox.checked).toEqual(false));
});
test('clicking Review and finish leads to Details', async () => {
test('clicking Review and finish leads to Review', async () => {
await renderCreateMode();
await goToRepositoriesStep();
await clickReviewAndFinish();
await screen.findByRole('heading', {
name: 'Details',
name: /Review/,
});
});

View file

@ -109,12 +109,12 @@ describe('repository snapshot tab - ', () => {
vi.clearAllMocks();
});
test('clicking Review and finish leads to Details', async () => {
test('clicking Review and finish leads to Review', async () => {
await renderCreateMode();
await goToSnapshotStep();
await clickReviewAndFinish();
await screen.findByRole('heading', {
name: 'Details',
name: /Review/i,
});
});