diff --git a/web/src/components/overview/OverviewPage.tsx b/web/src/components/overview/OverviewPage.tsx index 299f9081f6..f72e598b25 100644 --- a/web/src/components/overview/OverviewPage.tsx +++ b/web/src/components/overview/OverviewPage.tsx @@ -37,6 +37,7 @@ import { import { Link } from "react-router-dom"; import { Page } from "~/components/core"; import L10nSection from "./L10nSection"; +import StorageSection from "./StorageSection"; import SoftwareSection from "./SoftwareSection"; import { _ } from "~/i18n"; import { useAllIssues } from "~/queries/issues"; @@ -100,6 +101,7 @@ const OverviewSection = () => ( > + diff --git a/web/src/components/overview/StorageSection.test.tsx b/web/src/components/overview/StorageSection.test.tsx index 742ed461c5..bc85cd1fdd 100644 --- a/web/src/components/overview/StorageSection.test.tsx +++ b/web/src/components/overview/StorageSection.test.tsx @@ -24,27 +24,36 @@ import React from "react"; import { screen } from "@testing-library/react"; import { plainRender } from "~/test-utils"; import { StorageSection } from "~/components/overview"; +import * as ConfigModel from "~/storage/model/config"; -const mockAvailableDevices = [ +const mockDevices = [ { name: "/dev/sda", size: 536870912000 }, { name: "/dev/sdb", size: 697932185600 }, ]; -const mockResultSettings = { target: "disk", targetDevice: "/dev/sda", spacePolicy: "delete" }; +const mockConfig = { devices: [] as ConfigModel.Device[] }; jest.mock("~/queries/storage", () => ({ ...jest.requireActual("~/queries/storage"), - useAvailableDevices: () => mockAvailableDevices, - useProposalResult: () => ({ - settings: mockResultSettings, - actions: [], - }), + useDevices: () => mockDevices, + useConfigDevices: () => mockConfig.devices, })); -describe("when there is a proposal", () => { +describe("when the configuration does not include any device", () => { beforeEach(() => { - mockResultSettings.target = "disk"; - mockResultSettings.spacePolicy = "delete"; + mockConfig.devices = []; + }); + + it("indicates that a device is not selected", async () => { + plainRender(); + + await screen.findByText(/No device selected/); + }); +}); + +describe("when the configuration contains one drive", () => { + beforeEach(() => { + mockConfig.devices = [{ name: "/dev/sda", spacePolicy: "delete" }]; }); it("renders the proposal summary", async () => { @@ -57,9 +66,7 @@ describe("when there is a proposal", () => { describe("and the space policy is set to 'resize'", () => { beforeEach(() => { - // const result = { settings: { spacePolicy: "resize", targetDevice: "/dev/sda" } }; - mockResultSettings.spacePolicy = "resize"; - mockResultSettings.targetDevice = "/dev/sda"; + mockConfig.devices[0].spacePolicy = "resize"; }); it("indicates that partitions may be shrunk", async () => { @@ -71,8 +78,7 @@ describe("when there is a proposal", () => { describe("and the space policy is set to 'keep'", () => { beforeEach(() => { - mockResultSettings.spacePolicy = "keep"; - mockResultSettings.targetDevice = "/dev/sda"; + mockConfig.devices[0].spacePolicy = "keep"; }); it("indicates that partitions will be kept", async () => { @@ -82,9 +88,9 @@ describe("when there is a proposal", () => { }); }); - describe("and there is no target device", () => { + describe("and the drive matches no disk", () => { beforeEach(() => { - mockResultSettings.targetDevice = ""; + mockConfig.devices[0].name = null; }); it("indicates that a device is not selected", async () => { @@ -94,3 +100,19 @@ describe("when there is a proposal", () => { }); }); }); + +describe("when the configuration contains several drives", () => { + beforeEach(() => { + mockConfig.devices = [ + { name: "/dev/sda", spacePolicy: "delete" }, + { name: "/dev/sdb", spacePolicy: "delete" }, + ]; + }); + + it("renders the proposal summary", async () => { + plainRender(); + + await screen.findByText(/Install using several devices/); + await screen.findByText(/and deleting all its content/); + }); +}); diff --git a/web/src/components/overview/StorageSection.tsx b/web/src/components/overview/StorageSection.tsx index 3e514a03ba..5dad33f41c 100644 --- a/web/src/components/overview/StorageSection.tsx +++ b/web/src/components/overview/StorageSection.tsx @@ -25,75 +25,8 @@ import { Text, TextContent, TextVariants } from "@patternfly/react-core"; import { deviceLabel } from "~/components/storage/utils"; import { Em } from "~/components/core"; import { _ } from "~/i18n"; -import { useAvailableDevices, useProposalResult } from "~/queries/storage"; -import { ProposalTarget } from "~/types/storage"; - -/** - * Build a translated summary string for installing on an LVM with multiple - * physical partitions/disks - * @param policy - Find space policy - * @returns Translated description - */ -const msgLvmMultipleDisks = (policy: string): string => { - switch (policy) { - case "resize": - // TRANSLATORS: installing on an LVM with multiple physical partitions/disks - return _( - "Install in a new Logical Volume Manager (LVM) volume group shrinking existing partitions at the underlying devices as needed", - ); - case "keep": - // TRANSLATORS: installing on an LVM with multiple physical partitions/disks - return _( - "Install in a new Logical Volume Manager (LVM) volume group without modifying the partitions at the underlying devices", - ); - case "delete": - // TRANSLATORS: installing on an LVM with multiple physical partitions/disks - return _( - "Install in a new Logical Volume Manager (LVM) volume group deleting all the content of the underlying devices", - ); - case "custom": - // TRANSLATORS: installing on an LVM with multiple physical partitions/disks - return _( - "Install in a new Logical Volume Manager (LVM) volume group using a custom strategy to find the needed space at the underlying devices", - ); - } -}; - -/** - * Build a translated summary string for installing on an LVM with a single - * physical partition/disk - * @param policy - Find space policy - * @returns Translated description with %s placeholder for the device - * name - */ -const msgLvmSingleDisk = (policy: string): string => { - switch (policy) { - case "resize": - // TRANSLATORS: installing on an LVM with a single physical partition/disk, - // %s will be replaced by a device name and its size (eg. "/dev/sda, 20 GiB") - return _( - "Install in a new Logical Volume Manager (LVM) volume group on %s shrinking existing partitions as needed", - ); - case "keep": - // TRANSLATORS: installing on an LVM with a single physical partition/disk, - // %s will be replaced by a device name and its size (eg. "/dev/sda, 20 GiB") - return _( - "Install in a new Logical Volume Manager (LVM) volume group on %s without modifying existing partitions", - ); - case "delete": - // TRANSLATORS: installing on an LVM with a single physical partition/disk, - // %s will be replaced by a device name and its size (eg. "/dev/sda, 20 GiB") - return _( - "Install in a new Logical Volume Manager (LVM) volume group on %s deleting all its content", - ); - case "custom": - // TRANSLATORS: installing on an LVM with a single physical partition/disk, - // %s will be replaced by a device name and its size (eg. "/dev/sda, 20 GiB") - return _( - "Install in a new Logical Volume Manager (LVM) volume group on %s using a custom strategy to find the needed space", - ); - } -}; +import { useDevices, useConfigDevices } from "~/queries/storage"; +import * as ConfigModel from "~/storage/model/config"; const Content = ({ children }) => ( @@ -105,80 +38,74 @@ const Content = ({ children }) => ( /** * Text explaining the storage proposal * - * FIXME: this needs to be basically rewritten. See - * https://github.com/openSUSE/agama/discussions/778#discussioncomment-7715244 - * - * @param {object} props - * @param {Proposal} props.proposal + * TODO: The current implementation assumes there are only drives and no other kind of devices like + * LVM volume groups or MD raids. Support for more cases (like LVM installation) will be added as + * the rest of the interface is also adapted. */ export default function StorageSection() { - const availableDevices = useAvailableDevices(); - const result = useProposalResult(); + const drives = useConfigDevices(); + const devices = useDevices("system", { suspense: true }); - if (result === undefined) return; - - const label = (deviceName) => { - const device = availableDevices.find((d) => d.name === deviceName); - return device ? deviceLabel(device) : deviceName; + const label = (drive) => { + const device = devices.find((d) => d.name === drive.name); + return device ? deviceLabel(device) : drive.name; }; - if (result.settings.target === ProposalTarget.NEW_LVM_VG) { - const pvDevices = result.settings.targetPVDevices; - - if (pvDevices.length > 1) { - return ( - - {msgLvmMultipleDisks(result.settings.spacePolicy)} - - ); - } else { - const [msg1, msg2] = msgLvmSingleDisk(result.settings.spacePolicy).split("%s"); - - return ( - - - {msg1} - {label(pvDevices[0])} - {msg2} - - - ); - } - } - - const targetDevice = result.settings.targetDevice; - if (!targetDevice) return {_("No device selected yet")}; - - const fullMsg = (policy: string): string => { - switch (policy) { + const msgSingleDisk = (drive: ConfigModel.Device): string => { + switch (drive.spacePolicy) { case "resize": // TRANSLATORS: %s will be replaced by the device name and its size, // example: "/dev/sda, 20 GiB" - return _("Install using device %s shrinking existing partitions as needed"); + return _("Install using device %s shrinking existing partitions as needed."); case "keep": // TRANSLATORS: %s will be replaced by the device name and its size, // example: "/dev/sda, 20 GiB" - return _("Install using device %s without modifying existing partitions"); + return _("Install using device %s without modifying existing partitions."); case "delete": // TRANSLATORS: %s will be replaced by the device name and its size, // example: "/dev/sda, 20 GiB" - return _("Install using device %s and deleting all its content"); + return _("Install using device %s and deleting all its content."); } // TRANSLATORS: %s will be replaced by the device name and its size, // example: "/dev/sda, 20 GiB" - return _("Install using device %s with a custom strategy to find the needed space"); + return _("Install using device %s with a custom strategy to find the needed space."); }; - const [msg1, msg2] = fullMsg(result.settings.spacePolicy).split("%s"); + const msgMultipleDisks = (drives: ConfigModel.Device[]): string => { + if (drives.every((d) => d.spacePolicy === drives[0].spacePolicy)) { + switch (drives[0].spacePolicy) { + case "resize": + return _("Install using several devices shrinking existing partitions as needed."); + case "keep": + return _("Install using several devices without modifying existing partitions."); + case "delete": + return _("Install using several devices and deleting all its content."); + } + } - return ( - - - {msg1} - {label(targetDevice)} - {msg2} - - - ); + return _("Install using several devices with a custom strategy to find the needed space."); + }; + + if (drives.length === 0) return {_("No device selected yet")}; + + if (drives.length > 1) { + return ( + + {msgMultipleDisks(drives)} + + ); + } else { + const [msg1, msg2] = msgSingleDisk(drives[0]).split("%s"); + + return ( + + + {msg1} + {label(drives[0])} + {msg2} + + + ); + } } diff --git a/web/src/components/overview/index.js b/web/src/components/overview/index.js index a97896e9c6..6db707d5a2 100644 --- a/web/src/components/overview/index.js +++ b/web/src/components/overview/index.js @@ -22,4 +22,5 @@ export { default as OverviewPage } from "./OverviewPage"; export { default as L10nSection } from "./L10nSection"; +export { default as StorageSection } from "./StorageSection"; export { default as SoftwareSection } from "./SoftwareSection";