diff --git a/web/package/agama-web-ui.changes b/web/package/agama-web-ui.changes index 6b23968f83..e3cd032e56 100644 --- a/web/package/agama-web-ui.changes +++ b/web/package/agama-web-ui.changes @@ -1,3 +1,9 @@ +------------------------------------------------------------------- +Wed Mar 26 08:25:34 UTC 2025 - David Diaz + +- Change switches by checkboxes (gh#agama-project/agama#2168). +- Restructure the encryption form (gh#agama-project/agama#2168). + ------------------------------------------------------------------- Mon Mar 24 10:17:30 UTC 2025 - Imobach Gonzalez Sosa diff --git a/web/src/assets/styles/index.scss b/web/src/assets/styles/index.scss index 7529be7e40..ac9a8d8292 100644 --- a/web/src/assets/styles/index.scss +++ b/web/src/assets/styles/index.scss @@ -365,6 +365,12 @@ label.pf-m-disabled + .pf-v6-c-check__description { justify-self: flex-start; } +// Nested content inside forms should respect parent grid gap +.pf-v6-c-form [class*="pf-v6-u-mx"] { + display: grid; + gap: inherit; +} + // Some utilities not found at PF .w-14ch { inline-size: 14ch; diff --git a/web/src/components/storage/EncryptionSettingsPage.test.tsx b/web/src/components/storage/EncryptionSettingsPage.test.tsx index 38fa6dc462..9a32663447 100644 --- a/web/src/components/storage/EncryptionSettingsPage.test.tsx +++ b/web/src/components/storage/EncryptionSettingsPage.test.tsx @@ -21,7 +21,7 @@ */ import React from "react"; -import { screen, fireEvent } from "@testing-library/react"; +import { screen } from "@testing-library/react"; import { installerRender } from "~/test-utils"; import EncryptionSettingsPage from "./EncryptionSettingsPage"; import { EncryptionHook } from "~/queries/storage/config-model"; @@ -78,13 +78,13 @@ describe("EncryptionSettingsPage", () => { it("allows enabling the encryption", async () => { const { user } = installerRender(); - const toggle = screen.getByRole("switch", { name: "Encrypt the system" }); - expect(toggle).not.toBeChecked(); - await user.click(toggle); + const encryptionCheckbox = screen.getByRole("checkbox", { name: "Encrypt the system" }); + expect(encryptionCheckbox).not.toBeChecked(); + await user.click(encryptionCheckbox); const passwordInput = screen.getByLabelText("Password"); const passwordConfirmationInput = screen.getByLabelText("Password confirmation"); - fireEvent.change(passwordInput, { target: { value: "12345" } }); - fireEvent.change(passwordConfirmationInput, { target: { value: "12345" } }); + await user.type(passwordInput, "12345"); + await user.type(passwordConfirmationInput, "12345"); const acceptButton = screen.getByRole("button", { name: "Accept" }); await user.click(acceptButton); expect(mockNoEncryption.enable).toHaveBeenCalledWith("luks2", "12345"); @@ -96,25 +96,15 @@ describe("EncryptionSettingsPage", () => { mockUseEncryption.mockReturnValue(mockLuks2Encryption); }); - describe("and user chooses to not use encryption", () => { - it("allows disabling the encryption", async () => { - const { user } = installerRender(); - const toggle = screen.getByRole("switch", { name: "Encrypt the system" }); - expect(toggle).toBeChecked(); - await user.click(toggle); - const passwordInput = screen.getByLabelText("Password"); - const passwordConfirmationInput = screen.getByLabelText("Password confirmation"); - const tpmCheckbox = screen.getByRole("checkbox", { name: /Use.*TPM/ }); - - expect(passwordInput).toBeDisabled(); - expect(passwordConfirmationInput).toBeDisabled(); - expect(tpmCheckbox).toBeDisabled(); - - const acceptButton = screen.getByRole("button", { name: "Accept" }); - await user.click(acceptButton); - - expect(mockLuks2Encryption.disable).toHaveBeenCalled(); - }); + it("allows disabling the encryption", async () => { + const { user } = installerRender(); + const encryptionCheckbox = screen.getByRole("checkbox", { name: "Encrypt the system" }); + expect(encryptionCheckbox).toBeChecked(); + await user.click(encryptionCheckbox); + const acceptButton = screen.getByRole("button", { name: "Accept" }); + await user.click(acceptButton); + + expect(mockLuks2Encryption.disable).toHaveBeenCalled(); }); }); @@ -142,7 +132,7 @@ describe("EncryptionSettingsPage", () => { it("does not offer TPM", () => { installerRender(); - expect(screen.queryByRole("checkbox", { name: /Use.*TPM/ })).not.toBeInTheDocument(); + expect(screen.queryByRole("checkbox", { name: /Use.*TPM/ })).toBeNull(); }); }); }); diff --git a/web/src/components/storage/EncryptionSettingsPage.tsx b/web/src/components/storage/EncryptionSettingsPage.tsx index bb4e938f34..31bbe551eb 100644 --- a/web/src/components/storage/EncryptionSettingsPage.tsx +++ b/web/src/components/storage/EncryptionSettingsPage.tsx @@ -20,10 +20,10 @@ * find current contact information at www.suse.com. */ -import React, { useState, useRef } from "react"; +import React, { useEffect, useState, useRef } from "react"; import { useNavigate } from "react-router-dom"; -import { ActionGroup, Alert, Checkbox, Content, Form, Switch } from "@patternfly/react-core"; -import { Page, PasswordAndConfirmationInput } from "~/components/core"; +import { ActionGroup, Alert, Checkbox, Content, Form } from "@patternfly/react-core"; +import { NestedContent, Page, PasswordAndConfirmationInput } from "~/components/core"; import { useEncryptionMethods } from "~/queries/storage"; import { useEncryption } from "~/queries/storage/config-model"; import { EncryptionMethod } from "~/api/storage/types/config-model"; @@ -46,7 +46,7 @@ export default function EncryptionSettingsPage() { const passwordRef = useRef(); const formId = "encryptionSettingsForm"; - React.useEffect(() => { + useEffect(() => { if (encryptionConfig) { setIsEnabled(true); setMethod(encryptionConfig.method); @@ -86,12 +86,10 @@ export default function EncryptionSettingsPage() { }; // TRANSLATORS: "Trusted Platform Module" is the name of the technology and TPM its abbreviation - const tpm_label = _( - "Use the Trusted Platform Module (TPM) to decrypt automatically on each boot", - ); + const tpmLabel = _("Use the Trusted Platform Module (TPM) to decrypt automatically on each boot"); // TRANSLATORS: The word 'directly' is key here. For example, booting to the installer media and then choosing // 'Boot from Hard Disk' from there will not work. Keep it sort (this is a hint in a form) but keep it clear. - const tpm_explanation = _( + const tpmExplanation = _( "The password will not be needed to boot and access the data if the \ TPM can verify the integrity of the system. TPM sealing requires the new system to be booted \ directly on its first run.", @@ -103,12 +101,6 @@ directly on its first run.", {_("Encryption settings")} - - {_( - "Full Disk Encryption (FDE) allows to protect the information stored \ -at the new file systems, including data, programs, and system files.", - )} - @@ -120,28 +112,36 @@ at the new file systems, including data, programs, and system files.", ))} )} - setIsEnabled(!isEnabled)} /> - - {isTpmAvailable && ( - + {isEnabled && ( + + + {isTpmAvailable && ( + + )} + )} diff --git a/web/src/components/users/RootUserForm.test.tsx b/web/src/components/users/RootUserForm.test.tsx index 184fb36229..28f163d8db 100644 --- a/web/src/components/users/RootUserForm.test.tsx +++ b/web/src/components/users/RootUserForm.test.tsx @@ -98,7 +98,7 @@ describe("RootUserForm", () => { it("allows clearing the password", async () => { const { user } = installerRender(); - const passwordToggle = screen.getByRole("switch", { name: "Use password" }); + const passwordToggle = screen.getByRole("checkbox", { name: "Use password" }); const acceptButton = screen.getByRole("button", { name: "Accept" }); expect(passwordToggle).toBeChecked(); await user.click(passwordToggle); @@ -111,7 +111,7 @@ describe("RootUserForm", () => { it("allows setting a public SSH Key ", async () => { const { user } = installerRender(); - const sshPublicKeyToggle = screen.getByRole("switch", { name: "Use public SSH Key" }); + const sshPublicKeyToggle = screen.getByRole("checkbox", { name: "Use public SSH Key" }); const acceptButton = screen.getByRole("button", { name: "Accept" }); await user.click(sshPublicKeyToggle); const sshPublicKeyInput = screen.getByRole("textbox", { name: "File upload" }); @@ -126,7 +126,7 @@ describe("RootUserForm", () => { it("does not allow setting an empty public SSH Key", async () => { const { user } = installerRender(); - const sshPublicKeyToggle = screen.getByRole("switch", { name: "Use public SSH Key" }); + const sshPublicKeyToggle = screen.getByRole("checkbox", { name: "Use public SSH Key" }); const acceptButton = screen.getByRole("button", { name: "Accept" }); await user.click(sshPublicKeyToggle); expect(sshPublicKeyToggle).toBeChecked(); @@ -139,7 +139,7 @@ describe("RootUserForm", () => { it("allows clearing the public SSH Key", async () => { mockPublicKey = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDM+ test@example"; const { user } = installerRender(); - const sshPublicKeyToggle = screen.getByRole("switch", { name: "Use public SSH Key" }); + const sshPublicKeyToggle = screen.getByRole("checkbox", { name: "Use public SSH Key" }); const acceptButton = screen.getByRole("button", { name: "Accept" }); expect(sshPublicKeyToggle).toBeChecked(); await user.click(sshPublicKeyToggle); @@ -158,7 +158,7 @@ describe("RootUserForm", () => { it("allows preserving it", async () => { const { user } = installerRender(); - const passwordToggle = screen.getByRole("switch", { name: "Use password" }); + const passwordToggle = screen.getByRole("checkbox", { name: "Use password" }); const acceptButton = screen.getByRole("button", { name: "Accept" }); expect(passwordToggle).toBeChecked(); screen.getByText("Using a hashed password."); @@ -170,7 +170,7 @@ describe("RootUserForm", () => { it("allows discarding it", async () => { const { user } = installerRender(); - const passwordToggle = screen.getByRole("switch", { name: "Use password" }); + const passwordToggle = screen.getByRole("checkbox", { name: "Use password" }); const acceptButton = screen.getByRole("button", { name: "Accept" }); expect(passwordToggle).toBeChecked(); await user.click(passwordToggle); diff --git a/web/src/components/users/RootUserForm.tsx b/web/src/components/users/RootUserForm.tsx index 9165ecffa9..7a45eb6e7b 100644 --- a/web/src/components/users/RootUserForm.tsx +++ b/web/src/components/users/RootUserForm.tsx @@ -25,16 +25,14 @@ import { ActionGroup, Alert, Button, - Card, - CardBody, + Checkbox, Content, FileUpload, Form, FormGroup, - Switch, } from "@patternfly/react-core"; import { useNavigate } from "react-router-dom"; -import { Page, PasswordAndConfirmationInput } from "~/components/core"; +import { NestedContent, Page, PasswordAndConfirmationInput } from "~/components/core"; import { useRootUser, useRootUserMutation } from "~/queries/users"; import { RootUser } from "~/types/users"; import { isEmpty } from "~/utils"; @@ -148,53 +146,47 @@ const RootUserForm = () => { ))} )} - toggleMethod("password")} - /> - } - > - {activeMethods.password && ( - - - {usingHashedPassword ? ( - - {_("Using a hashed password.")}{" "} - - - ) : ( - - )} - - - )} + + toggleMethod("password")} + /> + {activeMethods.password && ( + + {usingHashedPassword ? ( + + {_("Using a hashed password.")}{" "} + + + ) : ( + + )} + + )} - toggleMethod("sshPublicKey")} - /> - } - > + + toggleMethod("sshPublicKey")} + /> + + {activeMethods.sshPublicKey && ( - - - - - + + + )}