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";