From d77ac55482b7b647dbfee1411ccb741943f9c8ee Mon Sep 17 00:00:00 2001 From: regexowl Date: Mon, 6 Jan 2025 14:08:41 +0100 Subject: [PATCH] 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. --- .../CreateImageWizard/CreateImageWizard.tsx | 2 +- .../CreateImageWizard/steps/Details/index.tsx | 18 ++------ .../CreateImageWizard/steps/Review/index.tsx | 3 ++ .../utilities/generateDefaultName.ts | 17 -------- .../utilities/useGenerateDefaultName.ts | 42 +++++++++++++++++++ .../FileSystemConfiguration.test.tsx | 4 +- .../steps/FirstBoot/Firstboot.test.tsx | 4 +- .../steps/Oscap/Oscap.test.tsx | 4 +- .../steps/Packages/Packages.test.tsx | 4 +- .../steps/Registration/Registration.test.tsx | 4 +- .../steps/Repositories/Repositories.test.tsx | 4 +- .../steps/Snapshot/Snapshot.test.tsx | 4 +- 12 files changed, 64 insertions(+), 46 deletions(-) delete mode 100644 src/Components/CreateImageWizard/utilities/generateDefaultName.ts create mode 100644 src/Components/CreateImageWizard/utilities/useGenerateDefaultName.ts diff --git a/src/Components/CreateImageWizard/CreateImageWizard.tsx b/src/Components/CreateImageWizard/CreateImageWizard.tsx index f1c5b38d..f81211e3 100644 --- a/src/Components/CreateImageWizard/CreateImageWizard.tsx +++ b/src/Components/CreateImageWizard/CreateImageWizard.tsx @@ -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} > diff --git a/src/Components/CreateImageWizard/steps/Details/index.tsx b/src/Components/CreateImageWizard/steps/Details/index.tsx index 4eddd02c..fa29419f 100644 --- a/src/Components/CreateImageWizard/steps/Details/index.tsx +++ b/src/Components/CreateImageWizard/steps/Details/index.tsx @@ -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, name: string diff --git a/src/Components/CreateImageWizard/steps/Review/index.tsx b/src/Components/CreateImageWizard/steps/Review/index.tsx index f70ce01f..886f3b44 100644 --- a/src/Components/CreateImageWizard/steps/Review/index.tsx +++ b/src/Components/CreateImageWizard/steps/Review/index.tsx @@ -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 (
diff --git a/src/Components/CreateImageWizard/utilities/generateDefaultName.ts b/src/Components/CreateImageWizard/utilities/generateDefaultName.ts deleted file mode 100644 index 8c159415..00000000 --- a/src/Components/CreateImageWizard/utilities/generateDefaultName.ts +++ /dev/null @@ -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}`; -}; diff --git a/src/Components/CreateImageWizard/utilities/useGenerateDefaultName.ts b/src/Components/CreateImageWizard/utilities/useGenerateDefaultName.ts new file mode 100644 index 00000000..d73d7a85 --- /dev/null +++ b/src/Components/CreateImageWizard/utilities/useGenerateDefaultName.ts @@ -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 + }, []); +}; diff --git a/src/test/Components/CreateImageWizard/steps/FileSystemConfiguration/FileSystemConfiguration.test.tsx b/src/test/Components/CreateImageWizard/steps/FileSystemConfiguration/FileSystemConfiguration.test.tsx index fbfb2994..075c7e14 100644 --- a/src/test/Components/CreateImageWizard/steps/FileSystemConfiguration/FileSystemConfiguration.test.tsx +++ b/src/test/Components/CreateImageWizard/steps/FileSystemConfiguration/FileSystemConfiguration.test.tsx @@ -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, }); }); diff --git a/src/test/Components/CreateImageWizard/steps/FirstBoot/Firstboot.test.tsx b/src/test/Components/CreateImageWizard/steps/FirstBoot/Firstboot.test.tsx index 492bb84b..35d1bfe7 100644 --- a/src/test/Components/CreateImageWizard/steps/FirstBoot/Firstboot.test.tsx +++ b/src/test/Components/CreateImageWizard/steps/FirstBoot/Firstboot.test.tsx @@ -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, }); }); diff --git a/src/test/Components/CreateImageWizard/steps/Oscap/Oscap.test.tsx b/src/test/Components/CreateImageWizard/steps/Oscap/Oscap.test.tsx index 6df568ac..4e8c64c0 100644 --- a/src/test/Components/CreateImageWizard/steps/Oscap/Oscap.test.tsx +++ b/src/test/Components/CreateImageWizard/steps/Oscap/Oscap.test.tsx @@ -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, }); }); }); diff --git a/src/test/Components/CreateImageWizard/steps/Packages/Packages.test.tsx b/src/test/Components/CreateImageWizard/steps/Packages/Packages.test.tsx index 2953f544..6cd83f66 100644 --- a/src/test/Components/CreateImageWizard/steps/Packages/Packages.test.tsx +++ b/src/test/Components/CreateImageWizard/steps/Packages/Packages.test.tsx @@ -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, }); }); diff --git a/src/test/Components/CreateImageWizard/steps/Registration/Registration.test.tsx b/src/test/Components/CreateImageWizard/steps/Registration/Registration.test.tsx index b60bde5f..8f3c9797 100644 --- a/src/test/Components/CreateImageWizard/steps/Registration/Registration.test.tsx +++ b/src/test/Components/CreateImageWizard/steps/Registration/Registration.test.tsx @@ -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, }); }); diff --git a/src/test/Components/CreateImageWizard/steps/Repositories/Repositories.test.tsx b/src/test/Components/CreateImageWizard/steps/Repositories/Repositories.test.tsx index a7f90971..39d9ba91 100644 --- a/src/test/Components/CreateImageWizard/steps/Repositories/Repositories.test.tsx +++ b/src/test/Components/CreateImageWizard/steps/Repositories/Repositories.test.tsx @@ -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/, }); }); diff --git a/src/test/Components/CreateImageWizard/steps/Snapshot/Snapshot.test.tsx b/src/test/Components/CreateImageWizard/steps/Snapshot/Snapshot.test.tsx index edbdc31a..fd010c98 100644 --- a/src/test/Components/CreateImageWizard/steps/Snapshot/Snapshot.test.tsx +++ b/src/test/Components/CreateImageWizard/steps/Snapshot/Snapshot.test.tsx @@ -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, }); });