From 18022b1478e654344843a141c0cfffdaa14516e3 Mon Sep 17 00:00:00 2001 From: Katerina Koukiou Date: Thu, 12 Dec 2024 13:30:44 +0100 Subject: [PATCH] Parse new 'hidden_screen' configuration file options Previously we would be using the ISO variant (Boot, Live) from Anaconda configuration file and we would conditionalize the page visibility in the Web UI code according to that. With this commit, the pages can be hidden per-new option of the configuration file [1]. [1] https://github.com/rhinstaller/anaconda/pull/6047 --- src/components/AnacondaPage.jsx | 28 ++++++++++++++-- src/components/AnacondaWizard.jsx | 8 +++-- .../localization/InstallationLanguage.jsx | 11 ++----- src/components/review/ReviewConfiguration.jsx | 13 ++++---- src/components/steps.js | 32 +++++++++++++++++-- .../storage/InstallationDestination.jsx | 6 ++-- src/components/storage/InstallationMethod.jsx | 13 +++----- .../storage/InstallationScenario.jsx | 5 ++- src/components/users/Accounts.jsx | 4 +-- src/contexts/Common.jsx | 5 ++- test/check-language | 2 +- test/helpers/utils.py | 1 + 12 files changed, 85 insertions(+), 43 deletions(-) diff --git a/src/components/AnacondaPage.jsx b/src/components/AnacondaPage.jsx index a05efc8536..54f12aabd1 100644 --- a/src/components/AnacondaPage.jsx +++ b/src/components/AnacondaPage.jsx @@ -14,10 +14,24 @@ * You should have received a copy of the GNU Lesser General Public License * along with This program; If not, see . */ -import React, { cloneElement, useEffect, useRef, useState } from "react"; +import cockpit from "cockpit"; + +import React, { cloneElement, useContext, useEffect, useRef, useState } from "react"; import { Alert, Stack, Title } from "@patternfly/react-core"; -export const AnacondaPage = ({ children, isFormDisabled, setIsFormDisabled, step, title, usePageInit }) => { +import { OsReleaseContext } from "../contexts/Common.jsx"; + +const _ = cockpit.gettext; + +export const AnacondaPage = ({ + children, + isFirstScreen, + isFormDisabled, + setIsFormDisabled, + step, + title, + usePageInit, +}) => { const [stepNotification, setStepNotification] = useState(); const [showPage, setShowPage] = useState(!isFormDisabled); const showPageRef = useRef(showPage); @@ -43,9 +57,11 @@ export const AnacondaPage = ({ children, isFormDisabled, setIsFormDisabled, step return null; } + const titleElem = isFirstScreen ? : title; + return ( - {title && {title}} + {titleElem && {titleElem}} {stepNotification?.step === step && ); }; + +const InitialPageTitle = () => { + const osRelease = useContext(OsReleaseContext); + + return cockpit.format(_("Welcome. Let's install $0 now."), osRelease.REDHAT_SUPPORT_PRODUCT); +}; diff --git a/src/components/AnacondaWizard.jsx b/src/components/AnacondaWizard.jsx index ab2c36c933..5c8717c93f 100644 --- a/src/components/AnacondaWizard.jsx +++ b/src/components/AnacondaWizard.jsx @@ -27,7 +27,7 @@ import { WizardStep, } from "@patternfly/react-core"; -import { FooterContext, StorageContext, SystemTypeContext } from "../contexts/Common.jsx"; +import { FooterContext, StorageContext, SystemTypeContext, UserInterfaceContext } from "../contexts/Common.jsx"; import { AnacondaPage } from "./AnacondaPage.jsx"; import { AnacondaWizardFooter } from "./AnacondaWizardFooter.jsx"; @@ -42,6 +42,7 @@ export const AnacondaWizard = ({ currentStepId, dispatch, isFetching, onCritFail const [isFormValid, setIsFormValid] = useState(false); const { storageScenarioId } = useContext(StorageContext); const isBootIso = useContext(SystemTypeContext) === "BOOT_ISO"; + const userInterfaceConfig = useContext(UserInterfaceContext); const { path } = usePageLocation(); const componentProps = { @@ -53,7 +54,7 @@ export const AnacondaWizard = ({ currentStepId, dispatch, isFetching, onCritFail showStorage, }; - const stepsOrder = getSteps(isBootIso, storageScenarioId); + const stepsOrder = getSteps(userInterfaceConfig, isBootIso, storageScenarioId); const firstStepId = stepsOrder.filter(s => !s.isHidden)[0].id; const createSteps = (stepsOrder, componentProps) => { @@ -76,8 +77,9 @@ export const AnacondaWizard = ({ currentStepId, dispatch, isFetching, onCritFail setIsFormDisabled={setIsFormDisabled} step={s.id} title={s.title} + isFirstScreen={s.isFirstScreen} usePageInit={s.usePageInit}> - + ), ...stepProps diff --git a/src/components/localization/InstallationLanguage.jsx b/src/components/localization/InstallationLanguage.jsx index 6c860580d8..61a81df495 100644 --- a/src/components/localization/InstallationLanguage.jsx +++ b/src/components/localization/InstallationLanguage.jsx @@ -45,7 +45,7 @@ import { setLangCookie } from "../../helpers/language.js"; -import { LanguageContext, OsReleaseContext } from "../../contexts/Common.jsx"; +import { LanguageContext } from "../../contexts/Common.jsx"; import "./InstallationLanguage.scss"; @@ -326,17 +326,10 @@ const InstallationLanguage = ({ idPrefix, setIsFormValid, setStepNotification }) ); }; -const PageTitle = () => { - const osRelease = useContext(OsReleaseContext); - return cockpit.format(_("Welcome to $0"), osRelease.NAME); -}; - export class Page { - constructor (isBootIso) { + constructor () { this.component = InstallationLanguage; this.id = "installation-language"; - this.isHidden = !isBootIso; this.label = _("Welcome"); - this.title = ; } } diff --git a/src/components/review/ReviewConfiguration.jsx b/src/components/review/ReviewConfiguration.jsx index f316213248..24f45fdbbd 100644 --- a/src/components/review/ReviewConfiguration.jsx +++ b/src/components/review/ReviewConfiguration.jsx @@ -27,7 +27,7 @@ import { import { getDeviceChildren } from "../../helpers/storage.js"; -import { LanguageContext, OsReleaseContext, StorageContext, SystemTypeContext, UsersContext } from "../../contexts/Common.jsx"; +import { LanguageContext, OsReleaseContext, StorageContext, SystemTypeContext, UserInterfaceContext, UsersContext } from "../../contexts/Common.jsx"; import { useOriginalDevices, usePlannedActions } from "../../hooks/Storage.jsx"; @@ -65,6 +65,8 @@ const ReviewConfiguration = ({ setIsFormValid }) => { const localizationData = useContext(LanguageContext); const accounts = useContext(UsersContext); const { label: scenarioLabel } = useScenario(); + const userInterfaceConfig = useContext(UserInterfaceContext); + const hiddenScreens = userInterfaceConfig.hidden_screens || []; const isBootIso = useContext(SystemTypeContext) === "BOOT_ISO"; // Display custom footer @@ -103,19 +105,18 @@ const ReviewConfiguration = ({ setIsFormValid }) => { description={language ? language["native-name"].v : localizationData.language} /> - {isBootIso && - <> + {!hiddenScreens.includes("accounts") && - + } + {isBootIso && - - } + } diff --git a/src/components/steps.js b/src/components/steps.js index 92e30c432b..86bfa6bc82 100644 --- a/src/components/steps.js +++ b/src/components/steps.js @@ -1,5 +1,7 @@ import cockpit from "cockpit"; +import { debug } from "../helpers/log.js"; + import { Page as PageProgress } from "./installation/InstallationProgress.jsx"; import { Page as PageInstallationLanguage } from "./localization/InstallationLanguage.jsx"; import { Page as PageReviewConfiguration } from "./review/ReviewConfiguration.jsx"; @@ -10,8 +12,9 @@ import { Page as PageAccounts } from "./users/Accounts.jsx"; const _ = cockpit.gettext; -export const getSteps = (...args) => { +export const getSteps = (userInterfaceConfig, ...args) => { const mountPointMappingStep = new PageMountPointMapping(...args); + const hiddenScreens = userInterfaceConfig.hidden_screens || []; const stepsOrder = [ new PageInstallationLanguage(...args), new PageInstallationMethod(...args), @@ -26,5 +29,30 @@ export const getSteps = (...args) => { new PageReviewConfiguration(...args), new PageProgress(...args), ]; - return stepsOrder; + + /* Screens can be hidden in two ways: + * 1. Dynamically: Controlled by the 'Page.isHidden' method in individual components, + * e.g., see 'MountPointMapping.jsx'. + * 2. Statically: Configured via the 'hidden_screens' key in the 'anaconda.conf' + * For example, the 'Account Creation' screen is hidden in the 'Workstation' ISO + * because this step is handled by the 'Gnome Initial Setup' tool during the first boot. + */ + return stepsOrder + .filter(s => { + const isHidden = hiddenScreens.includes(s.id); + + if (isHidden) { + debug( + `Screen '${s.id}' will not be displayed because it is hidden by the Anaconda configuration file.` + ); + } + + return !isHidden; + }) + .map((s, i) => { + if (i === 0) { + s.isFirstScreen = true; + } + return s; + }); }; diff --git a/src/components/storage/InstallationDestination.jsx b/src/components/storage/InstallationDestination.jsx index 80b693bd19..c63ee61204 100644 --- a/src/components/storage/InstallationDestination.jsx +++ b/src/components/storage/InstallationDestination.jsx @@ -47,7 +47,7 @@ import { debug } from "../../helpers/log.js"; import { getDeviceChildren } from "../../helpers/storage.js"; import { checkIfArraysAreEqual } from "../../helpers/utils.js"; -import { StorageContext, SystemTypeContext } from "../../contexts/Common.jsx"; +import { StorageContext } from "../../contexts/Common.jsx"; import { useOriginalDevices, useOriginalExistingSystems } from "../../hooks/Storage.jsx"; @@ -237,11 +237,11 @@ const rescanDisks = (setIsRescanningDisks, dispatch, errorHandler) => { export const InstallationDestination = ({ dispatch, idPrefix, + isFirstScreen, onCritFail, setIsFormValid, }) => { const refUsableDisks = useRef(); - const isBootIso = useContext(SystemTypeContext) === "BOOT_ISO"; const { diskSelection } = useContext(StorageContext); const devices = useOriginalDevices(); @@ -276,7 +276,7 @@ export const InstallationDestination = ({ setIsFormValid(selectedDisksCnt > 0); }, [selectedDisksCnt, setIsFormValid]); - const headingLevel = isBootIso ? "h2" : "h3"; + const headingLevel = isFirstScreen ? "h3" : "h2"; return ( { }, [needsReset, setIsFormDisabled]); }; -const PageTitle = () => { - const osRelease = useContext(OsReleaseContext); - - return cockpit.format(_("Welcome. Let's install $0 now."), osRelease.REDHAT_SUPPORT_PRODUCT); -}; - export class Page { - constructor (isBootIso) { + constructor () { this.component = InstallationMethod; this.id = "installation-method"; this.label = _("Installation method"); - this.title = !isBootIso ? : null; this.usePageInit = usePageInit; } } diff --git a/src/components/storage/InstallationScenario.jsx b/src/components/storage/InstallationScenario.jsx index 14246215ea..f700403f5d 100644 --- a/src/components/storage/InstallationScenario.jsx +++ b/src/components/storage/InstallationScenario.jsx @@ -36,7 +36,6 @@ import { AvailabilityState } from "./scenarios/helpers.js"; import { StorageContext, StorageDefaultsContext, - SystemTypeContext } from "../../contexts/Common.jsx"; import { @@ -210,12 +209,12 @@ const InstallationScenarioSelector = ({ export const InstallationScenario = ({ dispatch, idPrefix, + isFirstScreen, isFormDisabled, setIsFormValid, showStorage, }) => { - const isBootIso = useContext(SystemTypeContext) === "BOOT_ISO"; - const headingLevel = isBootIso ? "h2" : "h3"; + const headingLevel = isFirstScreen ? "h3" : "h2"; const { diskSelection, storageScenarioId } = useContext(StorageContext); const devices = useOriginalDevices(); diff --git a/src/components/users/Accounts.jsx b/src/components/users/Accounts.jsx index ca8e8ca1be..745f3851f3 100644 --- a/src/components/users/Accounts.jsx +++ b/src/components/users/Accounts.jsx @@ -352,11 +352,9 @@ const CustomFooter = () => { }; export class Page { - constructor (isBootIso) { + constructor () { this.component = Accounts; this.id = "accounts"; - this.isHidden = !isBootIso; this.label = _("Create Account"); - this.title = null; } } diff --git a/src/contexts/Common.jsx b/src/contexts/Common.jsx index 2ab6117a09..b29651cda9 100644 --- a/src/contexts/Common.jsx +++ b/src/contexts/Common.jsx @@ -29,6 +29,7 @@ export const StorageDefaultsContext = createContext(null); export const SystemTypeContext = createContext(null); export const TargetSystemRootContext = createContext(null); export const UsersContext = createContext(null); +export const UserInterfaceContext = createContext(null); export const NetworkContext = createContext(null); export const DialogsContext = createContext(null); @@ -74,7 +75,9 @@ const SystemInfoContextWrapper = ({ children, conf, osRelease }) => { - {children} + + {children} + diff --git a/test/check-language b/test/check-language index b6efeb49c8..e4a7324fcd 100755 --- a/test/check-language +++ b/test/check-language @@ -90,7 +90,7 @@ class TestLanguage(VirtInstallMachineCase): i.open() # Expect the default language - this is en at this point - adjust the test when better guess is implemented - b.wait_in_text("h2", "Welcome to Fedora Linux") + b.wait_in_text("h2", "Welcome. Let's install Fedora now.") l.check_selected_locale("en_US") diff --git a/test/helpers/utils.py b/test/helpers/utils.py index 4acd47da2b..9a0801b453 100644 --- a/test/helpers/utils.py +++ b/test/helpers/utils.py @@ -30,6 +30,7 @@ def pretend_live_iso(test, installer): installer.steps.hidden_steps.extend([installer.steps.ACCOUNTS, installer.steps.WELCOME]) test.restore_file('/run/anaconda/anaconda.conf') test.machine.execute("sed -i 's/type = BOOT_ISO/type = LIVE_OS/g' /run/anaconda/anaconda.conf") + test.machine.execute("sed -i '/[anaconda]/a hidden_screens = installation-language accounts' /run/anaconda/anaconda.conf") def pretend_default_scheme(test, scheme): test.restore_file('/run/anaconda/anaconda.conf')