diff --git a/web/src/components/storage/ProposalFileSystemsSection.jsx b/web/src/components/storage/ProposalFileSystemsSection.jsx new file mode 100644 index 0000000000..0b0e5f632d --- /dev/null +++ b/web/src/components/storage/ProposalFileSystemsSection.jsx @@ -0,0 +1,80 @@ +/* + * Copyright (c) [2024] SUSE LLC + * + * All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, contact SUSE LLC. + * + * To contact SUSE LLC about this file by physical or electronic mail, you may + * find current contact information at www.suse.com. + */ + +import React from "react"; +import { _ } from "~/i18n"; +import { Section } from "~/components/core"; +import { ProposalVolumes } from "~/components/storage"; +import { noop } from "~/utils"; + +/** + * @typedef {import ("~/client/storage").ProposalManager.ProposalSettings} ProposalSettings + * @typedef {import ("~/client/storage").ProposalManager.Volume} Volume + */ + +/** + * Section for editing the proposal file systems + * @component + * + * @callback onChangeFn + * @param {object} settings + * + * @param {object} props + * @param {ProposalSettings} props.settings + * @param {Volume[]} [props.volumeTemplates=[]] + * @param {boolean} [props.isLoading=false] + * @param {onChangeFn} [props.onChange=noop] + * + */ +export default function ProposalFileSystemsSection({ + settings, + volumeTemplates = [], + isLoading = false, + onChange = noop +}) { + const { volumes = [] } = settings; + + const changeVolumes = (volumes) => { + onChange({ volumes }); + }; + + // Templates for already existing mount points are filtered out + const usefulTemplates = () => { + const mountPaths = volumes.map(v => v.mountPath); + return volumeTemplates.filter(t => ( + t.mountPath.length > 0 && !mountPaths.includes(t.mountPath) + )); + }; + + const encryption = settings.encryptionPassword !== undefined && settings.encryptionPassword.length > 0; + + return ( +
+ +
+ ); +} diff --git a/web/src/components/storage/ProposalFileSystemsSection.test.jsx b/web/src/components/storage/ProposalFileSystemsSection.test.jsx new file mode 100644 index 0000000000..f0d30f606f --- /dev/null +++ b/web/src/components/storage/ProposalFileSystemsSection.test.jsx @@ -0,0 +1,54 @@ +/* + * Copyright (c) [2024] SUSE LLC + * + * All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, contact SUSE LLC. + * + * To contact SUSE LLC about this file by physical or electronic mail, you may + * find current contact information at www.suse.com. + */ + +import React from "react"; +import { screen, within } from "@testing-library/react"; +import { plainRender } from "~/test-utils"; +import { ProposalFileSystemsSection } from "~/components/storage"; + +const props = { + settings: {}, + isLoading: false, + onChange: jest.fn() +}; + +describe("ProposalFileSystemsSection", () => { + it("renders a section holding file systems related stuff", () => { + plainRender(); + screen.getByRole("region", { name: "File systems" }); + }); + + it("requests a volume change when onChange callback is triggered", async () => { + const { user } = plainRender(); + const button = screen.getByRole("button", { name: "Actions" }); + + await user.click(button); + + const menu = screen.getByRole("menu"); + const reset = within(menu).getByRole("menuitem", { name: /Reset/ }); + + await user.click(reset); + + expect(props.onChange).toHaveBeenCalledWith( + expect.objectContaining({ volumes: expect.any(Array) }) + ); + }); +}); diff --git a/web/src/components/storage/ProposalPage.jsx b/web/src/components/storage/ProposalPage.jsx index faeb34f240..38a5193af7 100644 --- a/web/src/components/storage/ProposalPage.jsx +++ b/web/src/components/storage/ProposalPage.jsx @@ -33,6 +33,7 @@ import { ProposalSettingsSection, ProposalSpacePolicySection, ProposalDeviceSection, + ProposalFileSystemsSection } from "~/components/storage"; import { IDLE } from "~/client/status"; @@ -200,15 +201,6 @@ export default function ProposalPage() { }; const PageContent = () => { - // Templates for already existing mount points are filtered out - const usefulTemplates = () => { - const volumes = state.settings.volumes || []; - const mountPaths = volumes.map(v => v.mountPath); - return state.volumeTemplates.filter(t => ( - t.mountPath.length > 0 && !mountPaths.includes(t.mountPath) - )); - }; - return ( <> + { @@ -451,10 +447,6 @@ export default function ProposalSettingsSection({ onChange({ encryptionPassword: password, encryptionMethod: method }); }; - const changeVolumes = (volumes) => { - onChange({ volumes }); - }; - const encryption = settings.encryptionPassword !== undefined && settings.encryptionPassword.length > 0; return ( @@ -475,13 +467,6 @@ export default function ProposalSettingsSection({ isLoading={settings.encryptionPassword === undefined} onChange={changeEncryption} /> - ); diff --git a/web/src/components/storage/index.js b/web/src/components/storage/index.js index 5cccae8494..6c518c51a0 100644 --- a/web/src/components/storage/index.js +++ b/web/src/components/storage/index.js @@ -24,6 +24,7 @@ export { default as ProposalPageMenu } from "./ProposalPageMenu"; export { default as ProposalSettingsSection } from "./ProposalSettingsSection"; export { default as ProposalSpacePolicySection } from "./ProposalSpacePolicySection"; export { default as ProposalDeviceSection } from "./ProposalDeviceSection"; +export { default as ProposalFileSystemsSection } from "./ProposalFileSystemsSection"; export { default as ProposalActionsSection } from "./ProposalActionsSection"; export { default as ProposalVolumes } from "./ProposalVolumes"; export { default as DASDPage } from "./DASDPage";