Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

webui: clean up some code around AnacondaWizard component #5248

Merged
merged 5 commits into from
Oct 16, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 8 additions & 2 deletions ui/webui/src/components/AnacondaPage.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,19 @@
* You should have received a copy of the GNU Lesser General Public License
* along with This program; If not, see <http://www.gnu.org/licenses/>.
*/
import { Stack, Title } from "@patternfly/react-core";
import { Alert, Stack, Title } from "@patternfly/react-core";
import React from "react";

export const AnacondaPage = ({ title, children }) => {
export const AnacondaPage = ({ title, children, step, stepNotification }) => {
return (
<Stack hasGutter>
{title && <Title headingLevel="h2">{title}</Title>}
{stepNotification && stepNotification.step === step &&
<Alert
isInline
title={stepNotification.message}
variant="danger"
/>}
{children}
</Stack>
);
Expand Down
105 changes: 55 additions & 50 deletions ui/webui/src/components/AnacondaWizard.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,13 +35,14 @@ import {
WizardContextConsumer
} from "@patternfly/react-core/deprecated";

import { InstallationMethod } from "./storage/InstallationMethod.jsx";
import { AnacondaPage } from "./AnacondaPage.jsx";
import { InstallationMethod, getPageProps as getInstallationMethodProps } from "./storage/InstallationMethod.jsx";
import { getScenario, getDefaultScenario } from "./storage/InstallationScenario.jsx";
import { MountPointMapping } from "./storage/MountPointMapping.jsx";
import { DiskEncryption, getStorageEncryptionState } from "./storage/DiskEncryption.jsx";
import { InstallationLanguage } from "./localization/InstallationLanguage.jsx";
import { MountPointMapping, getPageProps as getMountPointMappingProps } from "./storage/MountPointMapping.jsx";
import { DiskEncryption, getStorageEncryptionState, getPageProps as getDiskEncryptionProps } from "./storage/DiskEncryption.jsx";
import { InstallationLanguage, getPageProps as getInstallationLanguageProps } from "./localization/InstallationLanguage.jsx";
import { InstallationProgress } from "./installation/InstallationProgress.jsx";
import { ReviewConfiguration, ReviewConfigurationConfirmModal } from "./review/ReviewConfiguration.jsx";
import { ReviewConfiguration, ReviewConfigurationConfirmModal, getPageProps as getReviewConfigurationProps } from "./review/ReviewConfiguration.jsx";
import { exitGui } from "../helpers/exit.js";
import { usePageLocation } from "hooks";
import {
Expand All @@ -53,14 +54,14 @@ import {
const _ = cockpit.gettext;
const N_ = cockpit.noop;

export const AnacondaWizard = ({ dispatch, isBootIso, osRelease, storageData, localizationData, onCritFail, onAddErrorNotification, title, conf }) => {
export const AnacondaWizard = ({ dispatch, isBootIso, osRelease, storageData, localizationData, onCritFail, title, conf }) => {
const [isFormDisabled, setIsFormDisabled] = useState(false);
const [isFormValid, setIsFormValid] = useState(false);
const [requiredMountPoints, setRequiredMountPoints] = useState();
const [reusePartitioning, setReusePartitioning] = useState(false);
const [stepNotification, setStepNotification] = useState();
const [isFormDisabled, setIsFormDisabled] = useState(false);
const [storageEncryption, setStorageEncryption] = useState(getStorageEncryptionState());
const [storageScenarioId, setStorageScenarioId] = useState(window.sessionStorage.getItem("storage-scenario-id") || getDefaultScenario().id);
const [reusePartitioning, setReusePartitioning] = useState(false);
const [requiredMountPoints, setRequiredMountPoints] = useState();

const availableDevices = useMemo(() => {
return Object.keys(storageData.devices);
Expand Down Expand Up @@ -94,35 +95,44 @@ export const AnacondaWizard = ({ dispatch, isBootIso, osRelease, storageData, lo
}
}, [localizationData]);
const stepsOrder = [
...(isBootIso
? [{
component: InstallationLanguage,
data: { dispatch, languages: localizationData.languages, language: localizationData.language, commonLocales: localizationData.commonLocales },
id: "installation-language",
label: _("Welcome"),
}]
: []),
{
component: InstallationLanguage,
data: { dispatch, languages: localizationData.languages, language: localizationData.language, commonLocales: localizationData.commonLocales },
...getInstallationLanguageProps({ isBootIso, osRelease })
},
{
component: InstallationMethod,
data: { deviceData: storageData.devices, diskSelection: storageData.diskSelection, dispatch },
id: "installation-method",
label: _("Installation method"),
data: {
deviceData: storageData.devices,
diskSelection: storageData.diskSelection,
dispatch,
storageScenarioId,
setStorageScenarioId: (scenarioId) => {
window.sessionStorage.setItem("storage-scenario-id", scenarioId);
setStorageScenarioId(scenarioId);
}
},
...getInstallationMethodProps({ isBootIso, osRelease })
},
{
id: "disk-configuration",
label: _("Disk configuration"),
steps: [{
component: MountPointMapping,
data: { deviceData: storageData.devices, diskSelection: storageData.diskSelection, partitioningData: storageData.partitioning, requiredMountPoints, dispatch, reusePartitioning, setReusePartitioning },
id: "mount-point-mapping",
label: _("Manual disk configuration"),
isHidden: storageScenarioId !== "mount-point-mapping"

data: {
deviceData: storageData.devices,
diskSelection: storageData.diskSelection,
dispatch,
partitioningData: storageData.partitioning,
requiredMountPoints,
reusePartitioning,
setReusePartitioning,
},
...getMountPointMappingProps({ storageScenarioId })
}, {
component: DiskEncryption,
id: "disk-encryption",
label: _("Disk encryption"),
isHidden: storageScenarioId === "mount-point-mapping"
data: { storageEncryption, setStorageEncryption },
...getDiskEncryptionProps({ storageScenarioId })
}]
},
{
Expand All @@ -133,10 +143,10 @@ export const AnacondaWizard = ({ dispatch, isBootIso, osRelease, storageData, lo
requests: storageData.partitioning ? storageData.partitioning.requests : null,
language,
localizationData,
osRelease
osRelease,
storageScenarioId,
},
id: "installation-review",
label: _("Review and install"),
...getReviewConfigurationProps()
},
{
component: InstallationProgress,
Expand Down Expand Up @@ -191,25 +201,20 @@ export const AnacondaWizard = ({ dispatch, isBootIso, osRelease, storageData, lo
step = ({
...step,
component: (
<s.component
idPrefix={s.id}
setIsFormValid={setIsFormValid}
onCritFail={onCritFail}
onAddErrorNotification={onAddErrorNotification}
stepNotification={stepNotification}
isFormDisabled={isFormDisabled}
setIsFormDisabled={setIsFormDisabled}
storageEncryption={storageEncryption}
setStorageEncryption={setStorageEncryption}
storageScenarioId={storageScenarioId}
isBootIso={isBootIso}
osRelease={osRelease}
setStorageScenarioId={(scenarioId) => {
window.sessionStorage.setItem("storage-scenario-id", scenarioId);
setStorageScenarioId(scenarioId);
}}
{...s.data}
/>
<AnacondaPage step={s.id} title={s.title} stepNotification={stepNotification}>
<s.component
idPrefix={s.id}
setIsFormValid={setIsFormValid}
onCritFail={onCritFail}
setStepNotification={ex => setStepNotification({ step: s.id, ...ex })}
stepNotification={stepNotification}
isFormDisabled={isFormDisabled}
setIsFormDisabled={setIsFormDisabled}
isBootIso={isBootIso}
osRelease={osRelease}
{...s.data}
/>
</AnacondaPage>
),
});
} else if (s.steps) {
Expand Down
37 changes: 0 additions & 37 deletions ui/webui/src/components/app.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ import cockpit from "cockpit";
import React, { useEffect, useState } from "react";

import {
AlertGroup, AlertVariant, AlertActionCloseButton, Alert,
Page, PageGroup,
} from "@patternfly/react-core";

Expand Down Expand Up @@ -51,7 +50,6 @@ export const Application = () => {
const [beta, setBeta] = useState();
const [conf, setConf] = useState();
const [language, setLanguage] = useState();
const [notifications, setNotifications] = useState({});
const [osRelease, setOsRelease] = useState("");
const [state, dispatch] = useReducerWithThunk(reducer, initialState);
const [storeInitilized, setStoreInitialized] = useState(false);
Expand Down Expand Up @@ -103,17 +101,6 @@ export const Application = () => {
readOsRelease().then(osRelease => setOsRelease(osRelease));
}, [dispatch]);

const onAddNotification = (notificationProps) => {
setNotifications({
...notifications,
[notifications.length]: { index: notifications.length, ...notificationProps }
});
};

const onAddErrorNotification = ex => {
onAddNotification({ title: ex.name, message: ex.message, variant: "danger" });
};

// Postpone rendering anything until we read the dbus address and the default configuration
if (!criticalError && (!address || !conf || beta === undefined || !osRelease || !storeInitilized)) {
debug("Loading initial data...");
Expand All @@ -136,29 +123,6 @@ export const Application = () => {
<Page
data-debug={conf.Anaconda.debug}
>
{Object.keys(notifications).length > 0 &&
<AlertGroup isToast isLiveRegion>
{Object.keys(notifications).map(idx => {
const notification = notifications[idx];
const newNotifications = { ...notifications };
delete newNotifications[notification.index];

return (
<Alert
variant={AlertVariant[notification.variant]}
title={notification.title}
actionClose={
<AlertActionCloseButton
title={notifications.title}
onClose={() => setNotifications(newNotifications)}
/>
}
key={notification.index}>
{notification.message}
</Alert>
);
})}
</AlertGroup>}
<PageGroup stickyOnBreakpoint={{ default: "top" }}>
<AnacondaHeader
beta={beta}
Expand All @@ -172,7 +136,6 @@ export const Application = () => {
<AnacondaWizard
isBootIso={isBootIso}
onCritFail={onCritFail}
onAddErrorNotification={onAddErrorNotification}
title={title}
storageData={state.storage}
localizationData={state.localization}
Expand Down
34 changes: 19 additions & 15 deletions ui/webui/src/components/localization/InstallationLanguage.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,6 @@ import {
Alert, MenuSearchInput,
} from "@patternfly/react-core";

import { read_os_release as readOsRelease } from "os-release.js";
import { AddressContext, LanguageContext } from "../Common.jsx";
import { setLocale } from "../../apis/boss.js";

Expand All @@ -46,7 +45,6 @@ import {
getLangCookie,
setLangCookie
} from "../../helpers/language.js";
import { AnacondaPage } from "../AnacondaPage.jsx";

import "./InstallationLanguage.scss";

Expand Down Expand Up @@ -78,7 +76,7 @@ class LanguageSelector extends React.Component {
}
setLocale({ locale: this.props.language });
} catch (e) {
this.props.onAddErrorNotification(e);
this.props.setStepNotification(e);
}
}

Expand Down Expand Up @@ -210,7 +208,10 @@ class LanguageSelector extends React.Component {
setLangCookie({ cockpitLang: convertToCockpitLang({ lang: getLocaleId(localeItem) }) });
setLanguage({ lang: getLocaleId(localeItem) })
.then(() => setLocale({ locale: getLocaleId(localeItem) }))
.catch(this.props.onAddErrorNotification);
.catch(ex => {
console.info({ ex });
this.props.setStepNotification(ex);
});
this.setState({ lang: item });
this.updateNativeName(localeItem);
fetch("po.js").then(response => response.text())
Expand Down Expand Up @@ -284,21 +285,15 @@ class LanguageSelector extends React.Component {
}
LanguageSelector.contextType = AddressContext;

export const InstallationLanguage = ({ idPrefix, languages, language, commonLocales, setIsFormValid, onAddErrorNotification }) => {
const [nativeName, setNativeName] = React.useState(false);
export const InstallationLanguage = ({ idPrefix, languages, language, commonLocales, setIsFormValid, setStepNotification }) => {
const [nativeName, setNativeName] = useState(false);
const { setLanguage } = React.useContext(LanguageContext);
const [distributionName, setDistributionName] = useState("");

useEffect(() => {
readOsRelease().then(osRelease => setDistributionName(osRelease.NAME));
}, []);

useEffect(() => {
setIsFormValid(language !== "");
}, [language, setIsFormValid]);

return (
<AnacondaPage title={cockpit.format(_("Welcome to $0"), distributionName)}>
<>
<Title
headingLevel="h3"
>
Expand All @@ -324,12 +319,21 @@ export const InstallationLanguage = ({ idPrefix, languages, language, commonLoca
commonLocales={commonLocales}
language={language}
setIsFormValid={setIsFormValid}
onAddErrorNotification={onAddErrorNotification}
setStepNotification={setStepNotification}
setNativeName={setNativeName}
reRenderApp={setLanguage}
/>
</FormGroup>
</Form>
</AnacondaPage>
</>
);
};

export const getPageProps = ({ isBootIso, osRelease }) => {
return ({
id: "installation-language",
label: _("Welcome"),
isHidden: !isBootIso,
title: cockpit.format(_("Welcome to $0"), osRelease.NAME),
});
};
14 changes: 10 additions & 4 deletions ui/webui/src/components/review/ReviewConfiguration.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,6 @@ import {
} from "../../apis/storage.js";
import { checkDeviceInSubTree } from "../../helpers/storage.js";

import { AnacondaPage } from "../AnacondaPage.jsx";

import { getScenario } from "../storage/InstallationScenario.jsx";

import "./ReviewConfiguration.scss";
Expand Down Expand Up @@ -116,7 +114,7 @@ export const ReviewConfiguration = ({ deviceData, diskSelection, language, local
}, [setIsFormValid]);

return (
<AnacondaPage title={_("Review and install")}>
<>
<ReviewDescriptionList>
<DescriptionListGroup>
<DescriptionListTerm>
Expand Down Expand Up @@ -172,7 +170,7 @@ export const ReviewConfiguration = ({ deviceData, diskSelection, language, local
</DescriptionListDescription>
</DescriptionListGroup>
</ReviewDescriptionList>
</AnacondaPage>
</>
);
};

Expand Down Expand Up @@ -209,3 +207,11 @@ export const ReviewConfigurationConfirmModal = ({ idPrefix, onNext, setNextWaits
</Modal>
);
};

export const getPageProps = () => {
return ({
id: "installation-review",
label: _("Review and install"),
title: _("Review and install")
});
};
Loading