Skip to content

Commit

Permalink
components: use AnacondaWizardFooter in all steps with useWizardFoote…
Browse files Browse the repository at this point in the history
…r hook

That allows components, to customize the footer, resulting in the
AnacondaWizard to be one step closer to be steps agnostic.
  • Loading branch information
KKoukiou committed Mar 11, 2024
1 parent b54b883 commit 9c0dfaf
Show file tree
Hide file tree
Showing 7 changed files with 177 additions and 152 deletions.
55 changes: 23 additions & 32 deletions src/components/AnacondaWizard.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ import { InstallationLanguage, getPageProps as getInstallationLanguageProps } fr
import { Accounts, getPageProps as getAccountsProps, getAccountsState } from "./users/Accounts.jsx";
import { InstallationProgress } from "./installation/InstallationProgress.jsx";
import { ReviewConfiguration, getPageProps as getReviewConfigurationProps } from "./review/ReviewConfiguration.jsx";
import { OsReleaseContext, SystemTypeContext } from "./Common.jsx";
import { FooterContext, OsReleaseContext, SystemTypeContext } from "./Common.jsx";
import { resetPartitioning } from "../apis/storage_partitioning.js";

const _ = cockpit.gettext;
Expand All @@ -46,11 +46,6 @@ export const AnacondaWizard = ({ dispatch, storageData, localizationData, onCrit
const [isFormValid, setIsFormValid] = useState(false);
const [reusePartitioning, setReusePartitioning] = useState(false);
const [stepNotification, setStepNotification] = useState();
const [storageEncryption, setStorageEncryption] = useState({
confirmPassword: storageData.partitioning?.requests[0].passphrase || "",
encrypt: storageData.partitioning?.requests[0]?.encrypted,
password: storageData.partitioning?.requests[0]?.passphrase || "",
});
const [storageScenarioId, setStorageScenarioId] = useState(window.localStorage.getItem("storage-scenario-id") || getDefaultScenario().id);
const [accounts, setAccounts] = useState(getAccountsState());
const [showWizard, setShowWizard] = useState(true);
Expand Down Expand Up @@ -98,7 +93,7 @@ export const AnacondaWizard = ({ dispatch, storageData, localizationData, onCrit
}
}
}, [localizationData]);
const stepsOrder = [
let stepsOrder = [
{
component: InstallationLanguage,
data: { commonLocales: localizationData.commonLocales, dispatch, language: localizationData.language, languages: localizationData.languages },
Expand Down Expand Up @@ -139,8 +134,7 @@ export const AnacondaWizard = ({ dispatch, storageData, localizationData, onCrit
}, {
component: DiskEncryption,
data: {
setStorageEncryption,
storageEncryption,
partitioningData: storageData.partitioning,
},
...getDiskEncryptionProps({ storageScenarioId })
}]
Expand All @@ -167,6 +161,7 @@ export const AnacondaWizard = ({ dispatch, storageData, localizationData, onCrit
...getReviewConfigurationProps({ storageScenarioId })
},
];
stepsOrder = stepsOrder.filter(step => !step.isHidden);

const componentProps = {
isFormDisabled,
Expand All @@ -190,7 +185,7 @@ export const AnacondaWizard = ({ dispatch, storageData, localizationData, onCrit
return stepIds;
};
const flattenedStepsIds = getFlattenedStepsIds(stepsOrder);
const firstStepId = stepsOrder.filter(step => !step.isHidden)[0].id;
const firstStepId = stepsOrder[0].id;

const isStepFollowedBy = (earlierStepId, laterStepId) => {
const earlierStepIdx = flattenedStepsIds.findIndex(s => s === earlierStepId);
Expand Down Expand Up @@ -290,28 +285,24 @@ export const AnacondaWizard = ({ dispatch, storageData, localizationData, onCrit

return (
<PageSection type={PageSectionTypes.wizard} variant={PageSectionVariants.light}>
<Wizard
id="installation-wizard"
isVisitRequired
startIndex={startIndex}
footer={<AnacondaWizardFooter
onCritFail={onCritFail}
isFormValid={isFormValid}
partitioning={storageData.partitioning?.path}
setIsFormValid={setIsFormValid}
setStepNotification={setStepNotification}
isFormDisabled={isFormDisabled}
setIsFormDisabled={setIsFormDisabled}
setShowWizard={setShowWizard}
stepsOrder={stepsOrder}
storageEncryption={storageEncryption}
storageScenarioId={storageScenarioId}
accounts={accounts}
/>}
onStepChange={((event, currentStep, prevStep) => goToStep(currentStep, prevStep))}
>
{steps}
</Wizard>
<FooterContext.Provider value={{
isFormDisabled,
isFormValid,
setIsFormDisabled,
setIsFormValid,
setShowWizard,
setStepNotification,
}}>
<Wizard
id="installation-wizard"
isVisitRequired
startIndex={startIndex}
footer={<AnacondaWizardFooter />}
onStepChange={((event, currentStep, prevStep) => goToStep(currentStep, prevStep))}
>
{steps}
</Wizard>
</FooterContext.Provider>
</PageSection>
);
};
144 changes: 31 additions & 113 deletions src/components/AnacondaWizardFooter.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,38 +26,51 @@ import {
useWizardContext
} from "@patternfly/react-core";

import { ReviewConfigurationConfirmModal } from "./review/ReviewConfiguration.jsx";
import { SystemTypeContext } from "./Common.jsx";
import { applyStorage } from "../apis/storage_partitioning.js";
import { applyAccounts } from "./users/Accounts.jsx";
import { FooterContext, SystemTypeContext } from "./Common.jsx";
import { exitGui } from "../helpers/exit.js";

const _ = cockpit.gettext;
const N_ = cockpit.noop;

export const AnacondaWizardFooterTemplate = ({
export const AnacondaWizardFooter = ({
extraActions,
currentStepProps,
isFirstScreen,
isFormDisabled,
isFormValid,
footerHelperText,
nextButtonText,
nextButtonVariant,
onNext,
setIsFormValid,
}) => {
const [quitWaitsConfirmation, setQuitWaitsConfirmation] = useState(false);
const { activeStep, goToNextStep, goToPrevStep } = useWizardContext();
const isFirstScreen = activeStep.index === 1;
const isBootIso = useContext(SystemTypeContext) === "BOOT_ISO";
const {
isFormDisabled,
isFormValid,
setIsFormDisabled,
setIsFormValid,
setStepNotification
} = useContext(FooterContext);

const onNextButtonClicked = () => {
if (onNext) {
onNext({
goToNextStep,
isFormDisabled,
isFormValid,
setIsFormDisabled,
setIsFormValid,
setStepNotification,
});
} else {
goToNextStep();
}
};

const onBack = () => {
// first reset validation state to default
setIsFormValid(true);
goToPrevStep();
};

const footerHelperText = currentStepProps?.footerHelperText;
const nextButtonText = currentStepProps?.nextButtonText || _("Next");
const nextButtonVariant = currentStepProps?.nextButtonVariant || "primary";

return (
<WizardFooterWrapper>
<Stack hasGutter>
Expand All @@ -78,10 +91,10 @@ export const AnacondaWizardFooterTemplate = ({
</Button>
<Button
id="installation-next-btn"
variant={nextButtonVariant}
variant={nextButtonVariant || "primary"}
isDisabled={!isFormValid || isFormDisabled}
onClick={() => onNext(activeStep, goToNextStep)}>
{nextButtonText}
onClick={onNextButtonClicked}>
{nextButtonText || _("Next")}
</Button>
<Button
id="installation-quit-btn"
Expand All @@ -100,101 +113,6 @@ export const AnacondaWizardFooterTemplate = ({
);
};

export const AnacondaWizardFooter = ({
onCritFail,
isFormValid,
setIsFormValid,
setStepNotification,
isFormDisabled,
partitioning,
setIsFormDisabled,
setShowWizard,
stepsOrder,
storageEncryption,
storageScenarioId,
accounts,
}) => {
const [nextWaitsConfirmation, setNextWaitsConfirmation] = useState(false);
const { activeStep } = useWizardContext();

const onNext = (activeStep, goToNextStep) => {
// first reset validation state to default
setIsFormValid(true);

if (activeStep.id === "disk-encryption") {
setIsFormDisabled(true);

applyStorage({
encrypt: storageEncryption.encrypt,
encryptPassword: storageEncryption.password,
onFail: ex => {
console.error(ex);
setIsFormDisabled(false);
setStepNotification({ step: activeStep.id, ...ex });
},
onSuccess: () => {
goToNextStep();

// Reset the state after the onNext call. Otherwise,
// React will try to render the current step again.
setIsFormDisabled(false);
setStepNotification();
},
});
} else if (activeStep.id === "installation-review") {
setNextWaitsConfirmation(true);
} else if (activeStep.id === "mount-point-mapping") {
setIsFormDisabled(true);

applyStorage({
onFail: ex => {
console.error(ex);
setIsFormDisabled(false);
setStepNotification({ step: activeStep.id, ...ex });
},
onSuccess: () => {
goToNextStep();

// Reset the state after the onNext call. Otherwise,
// React will try to render the current step again.
setIsFormDisabled(false);
setStepNotification();
},
partitioning,
});
} else if (activeStep.id === "accounts") {
applyAccounts(accounts)
.then(() => goToNextStep())
.catch(onCritFail({ context: N_("Account setting failed.") }));
} else {
goToNextStep();
}
};

const currentStep = stepsOrder.find(s => s.id === activeStep.id);
const isFirstScreen = stepsOrder.filter(step => !step.isHidden)[0].id === activeStep.id;

return (
<AnacondaWizardFooterTemplate
currentStepProps={currentStep}
extraActions={
activeStep.id === "installation-review" &&
nextWaitsConfirmation &&
<ReviewConfigurationConfirmModal
idPrefix={activeStep.id}
onNext={() => { setShowWizard(false); cockpit.location.go(["installation-progress"]) }}
setNextWaitsConfirmation={setNextWaitsConfirmation}
storageScenarioId={storageScenarioId}
/>
}
isFirstScreen={isFirstScreen}
isFormDisabled={isFormDisabled}
isFormValid={isFormValid}
onNext={onNext}
/>
);
};

export const QuitInstallationConfirmModal = ({ exitGui, setQuitWaitsConfirmation }) => {
const isBootIso = useContext(SystemTypeContext) === "BOOT_ISO";

Expand Down
1 change: 1 addition & 0 deletions src/components/Common.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import { Popover, PopoverPosition } from "@patternfly/react-core";

export const AddressContext = createContext("");
export const ConfContext = createContext();
export const FooterContext = createContext(null);
export const LanguageContext = createContext(null);
export const OsReleaseContext = createContext(null);
export const RuntimeContext = createContext(null);
Expand Down
34 changes: 32 additions & 2 deletions src/components/review/ReviewConfiguration.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,15 @@
* along with This program; If not, see <http://www.gnu.org/licenses/>.
*/
import cockpit from "cockpit";
import React, { useContext, useEffect, useState } from "react";
import React, { useContext, useEffect, useMemo, useState } from "react";
import {
Button,
DescriptionList, DescriptionListDescription,
DescriptionListGroup, DescriptionListTerm,
HelperText, HelperTextItem,
Modal, ModalVariant,
Stack,
useWizardFooter,
} from "@patternfly/react-core";

import { StorageReview } from "./StorageReview.jsx";
Expand All @@ -32,7 +33,8 @@ import {
getPartitioningRequest,
} from "../../apis/storage_partitioning.js";
import { getScenario } from "../storage/InstallationScenario.jsx";
import { OsReleaseContext } from "../Common.jsx";
import { FooterContext, OsReleaseContext } from "../Common.jsx";
import { AnacondaWizardFooter } from "../AnacondaWizardFooter.jsx";

import "./ReviewConfiguration.scss";

Expand Down Expand Up @@ -60,6 +62,10 @@ export const ReviewConfiguration = ({ deviceData, diskSelection, language, local
const [encrypt, setEncrypt] = useState();
const osRelease = useContext(OsReleaseContext);

// Display custom footer
const getFooter = useMemo(() => () => <CustomFooter storageScenarioId={storageScenarioId} />, [storageScenarioId]);
useWizardFooter(getFooter);

useEffect(() => {
const initializeEncrypt = async () => {
const partitioning = await getAppliedPartitioning().catch(console.error);
Expand Down Expand Up @@ -195,6 +201,30 @@ const ReviewConfigurationFooterHelperText = ({ storageScenarioId }) => {
);
};

const CustomFooter = ({ storageScenarioId }) => {
const [nextWaitsConfirmation, setNextWaitsConfirmation] = useState();
const { setShowWizard } = useContext(FooterContext);
const pageProps = getPageProps({ storageScenarioId });

return (
<>
{nextWaitsConfirmation &&
<ReviewConfigurationConfirmModal
idPrefix={pageProps.id}
onNext={() => { setShowWizard(false); cockpit.location.go(["installation-progress"]) }}
setNextWaitsConfirmation={setNextWaitsConfirmation}
storageScenarioId={storageScenarioId}
/>}
<AnacondaWizardFooter
footerHelperText={pageProps.footerHelperText}
nextButtonText={pageProps.nextButtonText}
nextButtonVariant={pageProps.nextButtonVariant}
onNext={() => nextWaitsConfirmation === undefined && setNextWaitsConfirmation(true)}
/>
</>
);
};

export const getPageProps = ({ storageScenarioId }) => {
return ({
footerHelperText: <ReviewConfigurationFooterHelperText storageScenarioId={storageScenarioId} />,
Expand Down
Loading

0 comments on commit 9c0dfaf

Please sign in to comment.