From d43bcc2dcfa19ca334e8d84762edbd61b5c31dca Mon Sep 17 00:00:00 2001 From: Lubos Kocman Date: Wed, 21 Aug 2024 20:48:53 +0200 Subject: [PATCH 01/20] Decorated product selection without radio btn * Resolves Issue #1415 * Entire card is clickable, selection of card is still marked by blue boarder * Add icon to structures, pass it to dbus, etc (please do not mix product icon with pattern icon) fallback to default.svg in case that no icon is set * Rework button layout to clickable layout * We might need to use table or similar in case that description is longer than the * Supply icon for all products including default icon --- products.d/agama-products.changes | 6 ++ products.d/leap_160.yaml | 1 + products.d/microos.yaml | 1 + products.d/sles_160.yaml | 1 + products.d/tumbleweed.yaml | 1 + rust/agama-lib/share/profile.schema.json | 1 + rust/agama-lib/src/product/client.rs | 7 ++ rust/package/agama.changes | 6 ++ service/lib/agama/dbus/software/product.rb | 9 +- service/lib/agama/software/product.rb | 8 ++ service/lib/agama/software/product_builder.rb | 3 + service/package/rubygem-agama-yast.changes | 6 ++ web/package/agama-web-ui.changes | 6 ++ web/src/assets/products/Leap16.svg | 79 +++++++++++++++++ web/src/assets/products/MicroOS.svg | 66 ++++++++++++++ web/src/assets/products/SUSE.svg | 86 +++++++++++++++++++ web/src/assets/products/Tumbleweed.svg | 66 ++++++++++++++ web/src/assets/products/default.svg | 76 ++++++++++++++++ .../product/ProductSelectionPage.jsx | 54 ++++++++---- web/src/types/software.ts | 2 + 20 files changed, 469 insertions(+), 16 deletions(-) create mode 100644 web/src/assets/products/Leap16.svg create mode 100644 web/src/assets/products/MicroOS.svg create mode 100644 web/src/assets/products/SUSE.svg create mode 100644 web/src/assets/products/Tumbleweed.svg create mode 100644 web/src/assets/products/default.svg diff --git a/products.d/agama-products.changes b/products.d/agama-products.changes index d46f75fd28..d817595222 100644 --- a/products.d/agama-products.changes +++ b/products.d/agama-products.changes @@ -1,3 +1,9 @@ +------------------------------------------------------------------- +Wed Aug 21 19:07:28 UTC 2024 - Lubos Kocman + +- Add product icons. Resolves Issue #1415 + Not setting any icon will default to default.svg icon + ------------------------------------------------------------------- Fri Aug 2 09:12:25 UTC 2024 - Imobach Gonzalez Sosa diff --git a/products.d/leap_160.yaml b/products.d/leap_160.yaml index 0a8e1cdddc..9d431580fa 100644 --- a/products.d/leap_160.yaml +++ b/products.d/leap_160.yaml @@ -8,6 +8,7 @@ name: Leap 16.0 Alpha description: 'Leap 16.0 is the latest version of a community distribution based on the latest SUSE Linux Enterprise Server.' # Do not manually change any translations! See README.md for more details. +icon: Leap16.svg translations: description: ca: El Leap 16.0 és la darrera versió d'una distribució comunitària basada en diff --git a/products.d/microos.yaml b/products.d/microos.yaml index f3a74a14c5..4e847af284 100644 --- a/products.d/microos.yaml +++ b/products.d/microos.yaml @@ -9,6 +9,7 @@ description: 'A quick, small distribution designed to host container workloads with automated administration & patching. openSUSE MicroOS provides transactional (atomic) updates upon a read-only btrfs root file system. As rolling release distribution the software is always up-to-date.' +icon: MicroOS.svg # Do not manually change any translations! See README.md for more details. translations: description: diff --git a/products.d/sles_160.yaml b/products.d/sles_160.yaml index 439505d0fb..0594745a3e 100644 --- a/products.d/sles_160.yaml +++ b/products.d/sles_160.yaml @@ -10,6 +10,7 @@ description: "SUSE Linux Enterprise Server is the open, reliable, compliant, and continuity. It is the secure and adaptable OS for long-term supported, innovation-ready infrastructure running business-critical workloads on-premises, in the cloud, and at the edge." +icon: SUSE.svg # Do not manually change any translations! See README.md for more details. translations: description: diff --git a/products.d/tumbleweed.yaml b/products.d/tumbleweed.yaml index 9b921f661f..839bcf0db9 100644 --- a/products.d/tumbleweed.yaml +++ b/products.d/tumbleweed.yaml @@ -9,6 +9,7 @@ description: 'The Tumbleweed distribution is a pure rolling release version of openSUSE containing the latest "stable" versions of all software instead of relying on rigid periodic release cycles. The project does this for users that want the newest stable software.' +icon: Tumbleweed.svg # Do not manually change any translations! See README.md for more details. translations: description: diff --git a/rust/agama-lib/share/profile.schema.json b/rust/agama-lib/share/profile.schema.json index 11381e2da1..29890553ae 100644 --- a/rust/agama-lib/share/profile.schema.json +++ b/rust/agama-lib/share/profile.schema.json @@ -30,6 +30,7 @@ "id": { "title": "Product identifier", "description": "The id field from a products.d/foo.yaml file", + "icon": "Product Icon path specified in products.d/foo.yaml file", "type": "string" }, "registrationCode": { diff --git a/rust/agama-lib/src/product/client.rs b/rust/agama-lib/src/product/client.rs index a75824e8f1..3283678694 100644 --- a/rust/agama-lib/src/product/client.rs +++ b/rust/agama-lib/src/product/client.rs @@ -16,6 +16,8 @@ pub struct Product { pub name: String, /// Product description pub description: String, + /// Product icon (e.g., "default.svg") + pub icon: String, } #[derive(Clone, Debug, Serialize, Deserialize, utoipa::ToSchema)] @@ -74,10 +76,15 @@ impl<'a> ProductClient<'a> { Some(value) => value.try_into().unwrap(), None => "", }; + let icon = match data.get("icon") { + Some(value) => value.try_into().unwrap(), + None => "default.svg", + }; Product { id, name, description: description.to_string(), + icon: icon.to_string(), } }) .collect(); diff --git a/rust/package/agama.changes b/rust/package/agama.changes index cbabd66d85..92207709a0 100644 --- a/rust/package/agama.changes +++ b/rust/package/agama.changes @@ -1,3 +1,9 @@ +------------------------------------------------------------------- +Wed Aug 21 19:07:28 UTC 2024 - Lubos Kocman + +- Add product icons. Resolves Issue #1415 + Not setting any icon will default to default.svg icon + ------------------------------------------------------------------- Thu Aug 15 08:33:02 UTC 2024 - Josef Reidinger diff --git a/service/lib/agama/dbus/software/product.rb b/service/lib/agama/dbus/software/product.rb index 1c85087b8c..53209ff0a3 100644 --- a/service/lib/agama/dbus/software/product.rb +++ b/service/lib/agama/dbus/software/product.rb @@ -54,7 +54,14 @@ def issues def available_products backend.products.map do |product| - [product.id, product.display_name, { "description" => product.localized_description }] + [ + product.id, + product.display_name, + { + "description" => product.localized_description, + "icon" => product.icon + } + ] end end diff --git a/service/lib/agama/software/product.rb b/service/lib/agama/software/product.rb index 5ea4e1fd69..8c4ed6b607 100644 --- a/service/lib/agama/software/product.rb +++ b/service/lib/agama/software/product.rb @@ -48,6 +48,13 @@ class Product # @return [String, nil] E.g., "1.0". attr_accessor :version + # Product icon. Please use specify filename with svg suffix and ensure referenced + # file exists inside agama/web/src/assests/product. + # default.svg unless be used nless specified otherwise. + # + # @return [String, "default.svg"] E.g., "1.0". + attr_accessor :icon + # List of repositories. # # @return [Array] Empty if the product requires registration. @@ -99,6 +106,7 @@ class Product # @param id [string] Product id. def initialize(id) @id = id + @icon = "default.svg" @repositories = [] @labels = [] @mandatory_packages = [] diff --git a/service/lib/agama/software/product_builder.rb b/service/lib/agama/software/product_builder.rb index d679f39dba..ba3e26f5de 100644 --- a/service/lib/agama/software/product_builder.rb +++ b/service/lib/agama/software/product_builder.rb @@ -64,6 +64,8 @@ def initialize_product(id, data, attrs) product.description = attrs["description"] product.name = data[:name] product.version = data[:version] + product.icon = attrs["icon"] if attrs.key?("icon") && !attrs["icon"].nil? + @logger.info(product.name + " uses icon " + product.icon) end end @@ -98,6 +100,7 @@ def product_data_from_config(id) { name: config.products.dig(id, "software", "base_product"), version: config.products.dig(id, "software", "version"), + icon: config.products.dig(id, "software", "icon"), labels: config.arch_elements_from( id, "software", "installation_labels", property: :label ), diff --git a/service/package/rubygem-agama-yast.changes b/service/package/rubygem-agama-yast.changes index ac41cda055..b004ea94e5 100644 --- a/service/package/rubygem-agama-yast.changes +++ b/service/package/rubygem-agama-yast.changes @@ -1,3 +1,9 @@ +------------------------------------------------------------------- +Wed Aug 21 19:07:28 UTC 2024 - Lubos Kocman + +- Add product icons. Resolves Issue #1415 + Not setting any icon will default to default.svg icon + ------------------------------------------------------------------- Mon Aug 19 15:13:46 UTC 2024 - Lubos Kocman diff --git a/web/package/agama-web-ui.changes b/web/package/agama-web-ui.changes index 3f0b2a424d..2627e92e18 100644 --- a/web/package/agama-web-ui.changes +++ b/web/package/agama-web-ui.changes @@ -1,3 +1,9 @@ +------------------------------------------------------------------- +Wed Aug 21 19:07:28 UTC 2024 - Lubos Kocman + +- Add product icons. Resolves Issue #1415 + Not setting any icon will default to default.svg icon + ------------------------------------------------------------------- Tue Aug 13 14:57:21 UTC 2024 - David Diaz diff --git a/web/src/assets/products/Leap16.svg b/web/src/assets/products/Leap16.svg new file mode 100644 index 0000000000..2ee4a146bd --- /dev/null +++ b/web/src/assets/products/Leap16.svg @@ -0,0 +1,79 @@ + + + + + + image/svg+xml + + + + + + + + + Alpha + diff --git a/web/src/assets/products/MicroOS.svg b/web/src/assets/products/MicroOS.svg new file mode 100644 index 0000000000..a177ef72f8 --- /dev/null +++ b/web/src/assets/products/MicroOS.svg @@ -0,0 +1,66 @@ + + + + + + image/svg+xml + + + + + + + + + + + + diff --git a/web/src/assets/products/SUSE.svg b/web/src/assets/products/SUSE.svg new file mode 100644 index 0000000000..3cb5d5c64c --- /dev/null +++ b/web/src/assets/products/SUSE.svg @@ -0,0 +1,86 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + diff --git a/web/src/assets/products/Tumbleweed.svg b/web/src/assets/products/Tumbleweed.svg new file mode 100644 index 0000000000..27ac560b1d --- /dev/null +++ b/web/src/assets/products/Tumbleweed.svg @@ -0,0 +1,66 @@ + + + + + + image/svg+xml + + + + + + + + + + + + diff --git a/web/src/assets/products/default.svg b/web/src/assets/products/default.svg new file mode 100644 index 0000000000..26c6d811e8 --- /dev/null +++ b/web/src/assets/products/default.svg @@ -0,0 +1,76 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + diff --git a/web/src/components/product/ProductSelectionPage.jsx b/web/src/components/product/ProductSelectionPage.jsx index a875e4b663..94ced960e7 100644 --- a/web/src/components/product/ProductSelectionPage.jsx +++ b/web/src/components/product/ProductSelectionPage.jsx @@ -1,3 +1,4 @@ +/* eslint @typescript-eslint/no-var-requires: "off" */ /* * Copyright (c) [2022-2024] SUSE LLC * @@ -20,7 +21,7 @@ */ import React, { useState } from "react"; -import { Card, CardBody, Flex, Form, Grid, GridItem, Radio } from "@patternfly/react-core"; +import { Card, CardBody, Flex, Form, Grid, GridItem } from "@patternfly/react-core"; import { Page } from "~/components/core"; import { Center } from "~/components/layout"; import { useConfigMutation, useProduct } from "~/queries/software"; @@ -46,16 +47,32 @@ function ProductSelectionPage() { } }; - const Item = ({ children }) => { + const Item = ({ children }) => ( + + {children} + + ); + + const ProductIcon = ({ src, alt }) => { + // Ensure that we display something even if icon path is incorrect + const productIcon = require(`../../assets/products/${src}`); + return ( - - {children} - + {alt} ); }; const isSelectionDisabled = !nextProduct || nextProduct === selectedProduct; + const handleCardClick = (product) => { + setNextProduct(product); + }; + return (
@@ -63,17 +80,24 @@ function ProductSelectionPage() { {products.map((product, index) => ( - + handleCardClick(product)} + style={{ + cursor: 'pointer', // Change the cursor to indicate clickable + border: nextProduct === product ? '2px solid #0066cc' : 'none', // Optional: highlight selected card + }} + > - {product.name}} - body={product.description} - isChecked={nextProduct === product} - onChange={() => setNextProduct(product)} + +
+ +

{product.description}

+
@@ -98,4 +122,4 @@ function ProductSelectionPage() { ); } -export default ProductSelectionPage; +export default ProductSelectionPage; \ No newline at end of file diff --git a/web/src/types/software.ts b/web/src/types/software.ts index 42a9d8edc9..355e1f6ac3 100644 --- a/web/src/types/software.ts +++ b/web/src/types/software.ts @@ -38,6 +38,8 @@ type Product = { name: string; /** Product description */ description: string; + /** Product icon (e.g., "default.svg") */ + icon: string; }; type PatternsSelection = { [key: string]: SelectedBy }; From 562fa73b6a541ed8b1e935ae861eee427cdfa147 Mon Sep 17 00:00:00 2001 From: Lubos Kocman Date: Wed, 21 Aug 2024 21:18:14 +0200 Subject: [PATCH 02/20] Do not repeat product name on the first line --- products.d/leap_160.yaml | 2 +- products.d/sles_160.yaml | 2 +- products.d/tumbleweed.yaml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/products.d/leap_160.yaml b/products.d/leap_160.yaml index 9d431580fa..b26db1ab92 100644 --- a/products.d/leap_160.yaml +++ b/products.d/leap_160.yaml @@ -5,7 +5,7 @@ name: Leap 16.0 Alpha # at the at translations/description key below to avoid using obsolete # translations!! # ------------------------------------------------------------------------------ -description: 'Leap 16.0 is the latest version of a community distribution based +description: 'The latest version of a community distribution based on the latest SUSE Linux Enterprise Server.' # Do not manually change any translations! See README.md for more details. icon: Leap16.svg diff --git a/products.d/sles_160.yaml b/products.d/sles_160.yaml index 0594745a3e..39b249a4a7 100644 --- a/products.d/sles_160.yaml +++ b/products.d/sles_160.yaml @@ -5,7 +5,7 @@ name: SUSE Linux Enteprise Server 16.0 Alpha # at the at translations/description key below to avoid using obsolete # translations!! # ------------------------------------------------------------------------------ -description: "SUSE Linux Enterprise Server is the open, reliable, compliant, and +description: "An open, reliable, compliant, and future-proof Linux Server choice that ensures the enterprise's business continuity. It is the secure and adaptable OS for long-term supported, innovation-ready infrastructure running business-critical workloads diff --git a/products.d/tumbleweed.yaml b/products.d/tumbleweed.yaml index 839bcf0db9..f8b182f2d7 100644 --- a/products.d/tumbleweed.yaml +++ b/products.d/tumbleweed.yaml @@ -5,7 +5,7 @@ name: openSUSE Tumbleweed # at the at translations/description key below to avoid using obsolete # translations!! # ------------------------------------------------------------------------------ -description: 'The Tumbleweed distribution is a pure rolling release version of +description: 'A pure rolling release version of openSUSE containing the latest "stable" versions of all software instead of relying on rigid periodic release cycles. The project does this for users that want the newest stable software.' From 51b84d7d4e8aade55b23a5e4caa3ba1f97d9fb9f Mon Sep 17 00:00:00 2001 From: Lubos Kocman Date: Wed, 21 Aug 2024 21:23:25 +0200 Subject: [PATCH 03/20] Add padding 20px from right as well as 20px from bottom to allow longer text without wrap --- web/src/components/product/ProductSelectionPage.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/src/components/product/ProductSelectionPage.jsx b/web/src/components/product/ProductSelectionPage.jsx index 94ced960e7..28c0849af7 100644 --- a/web/src/components/product/ProductSelectionPage.jsx +++ b/web/src/components/product/ProductSelectionPage.jsx @@ -62,7 +62,7 @@ function ProductSelectionPage() { src={productIcon} alt={alt} width="80px" - style={{ height: 'auto', width: '10%', float: 'left', padding: '0 15px 0 0' }} + style={{ height: 'auto', width: '10%', float: 'left', padding: '0 20px 20px 0' }} /> ); }; From 632a6523f9d767ec4f7e67f86e67564e9490b30e Mon Sep 17 00:00:00 2001 From: Lubos Kocman Date: Wed, 21 Aug 2024 21:45:53 +0200 Subject: [PATCH 04/20] Use border color that matches active button --- web/src/components/product/ProductSelectionPage.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/src/components/product/ProductSelectionPage.jsx b/web/src/components/product/ProductSelectionPage.jsx index 28c0849af7..a15f9326d6 100644 --- a/web/src/components/product/ProductSelectionPage.jsx +++ b/web/src/components/product/ProductSelectionPage.jsx @@ -86,7 +86,7 @@ function ProductSelectionPage() { onClick={() => handleCardClick(product)} style={{ cursor: 'pointer', // Change the cursor to indicate clickable - border: nextProduct === product ? '2px solid #0066cc' : 'none', // Optional: highlight selected card + border: nextProduct === product ? '2px solid #51c38d' : 'none', // highlight selected card }} > From 243d344e8ff06cbefca427f7909acbe6e7eb17d5 Mon Sep 17 00:00:00 2001 From: Lubos Kocman Date: Wed, 21 Aug 2024 22:06:50 +0200 Subject: [PATCH 05/20] get rid of problematic logger entry from test perspective --- service/lib/agama/software/product_builder.rb | 1 - 1 file changed, 1 deletion(-) diff --git a/service/lib/agama/software/product_builder.rb b/service/lib/agama/software/product_builder.rb index ba3e26f5de..bf3103ba1e 100644 --- a/service/lib/agama/software/product_builder.rb +++ b/service/lib/agama/software/product_builder.rb @@ -65,7 +65,6 @@ def initialize_product(id, data, attrs) product.name = data[:name] product.version = data[:version] product.icon = attrs["icon"] if attrs.key?("icon") && !attrs["icon"].nil? - @logger.info(product.name + " uses icon " + product.icon) end end From 43604fa4beeb3f2d0b2e1cca3c8a6848d2f143bc Mon Sep 17 00:00:00 2001 From: Lubos Kocman Date: Wed, 21 Aug 2024 22:12:16 +0200 Subject: [PATCH 06/20] add icon definition to ChangeProductLink test --- web/src/components/core/ChangeProductLink.test.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/web/src/components/core/ChangeProductLink.test.tsx b/web/src/components/core/ChangeProductLink.test.tsx index df69181a80..277635347f 100644 --- a/web/src/components/core/ChangeProductLink.test.tsx +++ b/web/src/components/core/ChangeProductLink.test.tsx @@ -30,11 +30,13 @@ const tumbleweedProduct = { id: "Tumbleweed", name: "openSUSE Tumbleweed", description: "Tumbleweed description...", + icon: "Tumbleweed.icon", }; const microosProduct = { id: "MicroOS", name: "openSUSE MicroOS", description: "MicroOS description", + icon: "MicroOS.icon", }; let mockUseProduct: { products: Product[]; selectedProduct?: Product }; From e7e7b9d0122979993ff79b5608b30eaf07e5778c Mon Sep 17 00:00:00 2001 From: Lubos Kocman Date: Thu, 22 Aug 2024 11:26:56 +0200 Subject: [PATCH 07/20] Update web/src/components/product/ProductSelectionPage.jsx MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: José Iván López --- web/src/components/product/ProductSelectionPage.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/src/components/product/ProductSelectionPage.jsx b/web/src/components/product/ProductSelectionPage.jsx index a15f9326d6..9043e26db7 100644 --- a/web/src/components/product/ProductSelectionPage.jsx +++ b/web/src/components/product/ProductSelectionPage.jsx @@ -55,7 +55,7 @@ function ProductSelectionPage() { const ProductIcon = ({ src, alt }) => { // Ensure that we display something even if icon path is incorrect - const productIcon = require(`../../assets/products/${src}`); + const productIcon = require(`~/assets/products/${src}`); return ( Date: Thu, 22 Aug 2024 11:27:12 +0200 Subject: [PATCH 08/20] Update service/lib/agama/software/product.rb Co-authored-by: Josef Reidinger --- service/lib/agama/software/product.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/service/lib/agama/software/product.rb b/service/lib/agama/software/product.rb index 8c4ed6b607..acae75757c 100644 --- a/service/lib/agama/software/product.rb +++ b/service/lib/agama/software/product.rb @@ -50,7 +50,7 @@ class Product # Product icon. Please use specify filename with svg suffix and ensure referenced # file exists inside agama/web/src/assests/product. - # default.svg unless be used nless specified otherwise. + # `default.svg` will be used unless specified otherwise. # # @return [String, "default.svg"] E.g., "1.0". attr_accessor :icon From ca591334c08e9b5d1593a28973e90d4dedddd77a Mon Sep 17 00:00:00 2001 From: Lubos Kocman Date: Thu, 22 Aug 2024 11:27:18 +0200 Subject: [PATCH 09/20] Update service/lib/agama/software/product_builder.rb Co-authored-by: Josef Reidinger --- service/lib/agama/software/product_builder.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/service/lib/agama/software/product_builder.rb b/service/lib/agama/software/product_builder.rb index bf3103ba1e..a9ff1d655b 100644 --- a/service/lib/agama/software/product_builder.rb +++ b/service/lib/agama/software/product_builder.rb @@ -64,7 +64,7 @@ def initialize_product(id, data, attrs) product.description = attrs["description"] product.name = data[:name] product.version = data[:version] - product.icon = attrs["icon"] if attrs.key?("icon") && !attrs["icon"].nil? + product.icon = attrs["icon"] if attrs["icon"] end end From 3c92e8dc2785bbf781e0ee19d049f62cb3590cf0 Mon Sep 17 00:00:00 2001 From: Lubos Kocman Date: Thu, 22 Aug 2024 11:27:33 +0200 Subject: [PATCH 10/20] Update service/lib/agama/software/product.rb Co-authored-by: Josef Reidinger --- service/lib/agama/software/product.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/service/lib/agama/software/product.rb b/service/lib/agama/software/product.rb index acae75757c..29262ce1ee 100644 --- a/service/lib/agama/software/product.rb +++ b/service/lib/agama/software/product.rb @@ -52,7 +52,7 @@ class Product # file exists inside agama/web/src/assests/product. # `default.svg` will be used unless specified otherwise. # - # @return [String, "default.svg"] E.g., "1.0". + # @return [String] E.g. "leap.svg" attr_accessor :icon # List of repositories. From f8ae4e7baf7223190419fce34f6dddb78cc03bca Mon Sep 17 00:00:00 2001 From: Lubos Kocman Date: Thu, 22 Aug 2024 12:08:23 +0200 Subject: [PATCH 11/20] Revert "Update web/src/components/product/ProductSelectionPage.jsx" Breaks build ERROR in ./src/components/product/ProductSelectionPage.jsx 107:22-63 Module not found: Error: Can't resolve '~/assets/products' in '/home/runn This reverts commit e7e7b9d0122979993ff79b5608b30eaf07e5778c. --- web/src/components/product/ProductSelectionPage.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/src/components/product/ProductSelectionPage.jsx b/web/src/components/product/ProductSelectionPage.jsx index 9043e26db7..a15f9326d6 100644 --- a/web/src/components/product/ProductSelectionPage.jsx +++ b/web/src/components/product/ProductSelectionPage.jsx @@ -55,7 +55,7 @@ function ProductSelectionPage() { const ProductIcon = ({ src, alt }) => { // Ensure that we display something even if icon path is incorrect - const productIcon = require(`~/assets/products/${src}`); + const productIcon = require(`../../assets/products/${src}`); return ( Date: Wed, 4 Sep 2024 13:30:37 +0100 Subject: [PATCH 12/20] fix(web): restore radio inputs in product selection Since it's arguably that they might confuse users and also because of consistent with the rest of the UI. They could be drop or hidden later, but thinking globally in all selectors and implications of doing so. Also, this commit improves a little the proposed layout by making use of the label for "increasing" the clickable area instead of adding a handler for the PF/Card component itself. Again, this can be refactored later if really needed. Read the reasoning at https://github.com/openSUSE/agama/pull/1550#pullrequestreview-2269021089 and/or https://github.com/openSUSE/agama/pull/1550#issuecomment-2326622683. --- web/src/assets/styles/app.scss | 8 ++++ .../product/ProductSelectionPage.jsx | 39 ++++++++++--------- 2 files changed, 28 insertions(+), 19 deletions(-) diff --git a/web/src/assets/styles/app.scss b/web/src/assets/styles/app.scss index c8dfab146e..509cba4a17 100644 --- a/web/src/assets/styles/app.scss +++ b/web/src/assets/styles/app.scss @@ -35,3 +35,11 @@ button.remove-link:hover { position: relative; width: 100%; } + +#productSelectionForm { + .pf-v5-c-radio input { + align-self: center; + width: 20px; + height: 20px; + } +} diff --git a/web/src/components/product/ProductSelectionPage.jsx b/web/src/components/product/ProductSelectionPage.jsx index a15f9326d6..d223ee7d56 100644 --- a/web/src/components/product/ProductSelectionPage.jsx +++ b/web/src/components/product/ProductSelectionPage.jsx @@ -21,7 +21,7 @@ */ import React, { useState } from "react"; -import { Card, CardBody, Flex, Form, Grid, GridItem } from "@patternfly/react-core"; +import { Card, CardBody, Flex, Form, Grid, GridItem, Radio, Split, Stack } from "@patternfly/react-core"; import { Page } from "~/components/core"; import { Center } from "~/components/layout"; import { useConfigMutation, useProduct } from "~/queries/software"; @@ -62,17 +62,12 @@ function ProductSelectionPage() { src={productIcon} alt={alt} width="80px" - style={{ height: 'auto', width: '10%', float: 'left', padding: '0 20px 20px 0' }} /> ); }; const isSelectionDisabled = !nextProduct || nextProduct === selectedProduct; - const handleCardClick = (product) => { - setNextProduct(product); - }; - return (
@@ -83,21 +78,27 @@ function ProductSelectionPage() { handleCardClick(product)} - style={{ - cursor: 'pointer', // Change the cursor to indicate clickable - border: nextProduct === product ? '2px solid #51c38d' : 'none', // highlight selected card - }} > - + + + +

{product.description}

+
+ + } + isChecked={nextProduct === product} + onChange={() => setNextProduct(product)} /> -
- -

{product.description}

-
@@ -122,4 +123,4 @@ function ProductSelectionPage() { ); } -export default ProductSelectionPage; \ No newline at end of file +export default ProductSelectionPage; From 3cb7ebf6ae691317f2bb4290ee79062967247709 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20D=C3=ADaz=20Gonz=C3=A1lez?= Date: Wed, 4 Sep 2024 16:29:26 +0100 Subject: [PATCH 13/20] fix(web): do not lost focus when selecting a product As a result of an internal component defined in the body of the main component (aka "render function"), the focus was lost each time the main component got re-rendered because of a "selected product" changed. This happens because these internal components are actually not re-rendered but remounting since they were re-defined :/ A bit messy, but there is a lot of documentation about it. To link a few, * https://react.dev/learn/preserving-and-resetting-state#different-components-at-the-same-position-reset-state - Which contains a "Pitfall" warning that says: **This is why you should not nest component function definitions.** * https://www.developerway.com/posts/react-re-renders-guide#part3.1 --- web/src/assets/styles/app.scss | 6 ++ .../product/ProductSelectionPage.jsx | 101 ++++++++---------- 2 files changed, 52 insertions(+), 55 deletions(-) diff --git a/web/src/assets/styles/app.scss b/web/src/assets/styles/app.scss index 509cba4a17..e5c8eafd40 100644 --- a/web/src/assets/styles/app.scss +++ b/web/src/assets/styles/app.scss @@ -42,4 +42,10 @@ button.remove-link:hover { width: 20px; height: 20px; } + + label { + img { + width: 80px; + } + } } diff --git a/web/src/components/product/ProductSelectionPage.jsx b/web/src/components/product/ProductSelectionPage.jsx index d223ee7d56..18b141cd69 100644 --- a/web/src/components/product/ProductSelectionPage.jsx +++ b/web/src/components/product/ProductSelectionPage.jsx @@ -1,4 +1,3 @@ -/* eslint @typescript-eslint/no-var-requires: "off" */ /* * Copyright (c) [2022-2024] SUSE LLC * @@ -25,16 +24,49 @@ import { Card, CardBody, Flex, Form, Grid, GridItem, Radio, Split, Stack } from import { Page } from "~/components/core"; import { Center } from "~/components/layout"; import { useConfigMutation, useProduct } from "~/queries/software"; -import { _ } from "~/i18n"; import styles from "@patternfly/react-styles/css/utilities/Text/text"; +import { sprintf } from "sprintf-js"; +import { _ } from "~/i18n"; -const Label = ({ children }) => ( - {children} +const ResponsiveGridItem = ({ children }) => ( + + {children} + ); +const Option = ({ product, isChecked, onChange }) => { + const logoSrc = `assets/logos/${product.icon}`; + // TRANSLATORS: %s will be replaced by a product name. E.g., "openSUSE Tumbleweed" + const logoAltText = sprintf(_("%s logo"), product.name); + + return ( + + + + + {logoAltText} + + {product.name} +

{product.description}

+
+ + } + isChecked={isChecked} + onChange={onChange} + /> +
+
+
+ ); +}; + function ProductSelectionPage() { - const { products, selectedProduct } = useProduct({ suspense: true }); const setConfig = useConfigMutation(); + const { products, selectedProduct } = useProduct({ suspense: true }); const [nextProduct, setNextProduct] = useState(selectedProduct); const [isLoading, setIsLoading] = useState(false); @@ -47,25 +79,6 @@ function ProductSelectionPage() { } }; - const Item = ({ children }) => ( - - {children} - - ); - - const ProductIcon = ({ src, alt }) => { - // Ensure that we display something even if icon path is incorrect - const productIcon = require(`../../assets/products/${src}`); - - return ( - {alt} - ); - }; - const isSelectionDisabled = !nextProduct || nextProduct === selectedProduct; return ( @@ -74,36 +87,14 @@ function ProductSelectionPage() {
{products.map((product, index) => ( - - - - - - - -

{product.description}

-
- - } - isChecked={nextProduct === product} - onChange={() => setNextProduct(product)} - /> -
-
-
+
-
+ ); } From e85aef007dc6f0b8d0c1a08885bff2aa8b3be67f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20D=C3=ADaz=20Gonz=C3=A1lez?= Date: Wed, 4 Sep 2024 16:50:37 +0100 Subject: [PATCH 14/20] feat(web): copy product logos to public dist directory To make them accessible. --- web/webpack.config.js | 1 + 1 file changed, 1 insertion(+) diff --git a/web/webpack.config.js b/web/webpack.config.js index fd82d21e93..2b726d2e4e 100644 --- a/web/webpack.config.js +++ b/web/webpack.config.js @@ -40,6 +40,7 @@ const copy_files = [ "./src/index.html", // TODO: consider using something more complete like https://github.com/jantimon/favicons-webpack-plugin "./src/assets/favicon.svg", + { from: "./src/assets/products/*.svg", to: "assets/logos/[name][ext]" } ]; const plugins = [ From ec57cc150a298e32e0328fe57fdc7bd09093a5d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20D=C3=ADaz=20Gonz=C3=A1lez?= Date: Wed, 4 Sep 2024 20:00:43 +0100 Subject: [PATCH 15/20] refactor(web): rework product selector For improving its a11y when using an screen reader. Also makes the PF/Card element clickable as proposed by @lkocman initially ease the selection for mouse users, but keeping the radio inputs as both, visual and keyboard aid. --- web/src/assets/styles/app.scss | 2 +- web/src/assets/styles/utilities.scss | 4 ++ .../product/ProductSelectionPage.jsx | 64 +++++++++++-------- 3 files changed, 42 insertions(+), 28 deletions(-) diff --git a/web/src/assets/styles/app.scss b/web/src/assets/styles/app.scss index e5c8eafd40..6fcffb5cb0 100644 --- a/web/src/assets/styles/app.scss +++ b/web/src/assets/styles/app.scss @@ -43,7 +43,7 @@ button.remove-link:hover { height: 20px; } - label { + .pf-v5-c-card { img { width: 80px; } diff --git a/web/src/assets/styles/utilities.scss b/web/src/assets/styles/utilities.scss index 37aad08505..3205abd5a0 100644 --- a/web/src/assets/styles/utilities.scss +++ b/web/src/assets/styles/utilities.scss @@ -132,6 +132,10 @@ 100% 16px; } +.cursor-pointer { + cursor: pointer; +} + // FIXME: drop as soon as Tip component gets rethought / refactored .label-tip .pf-v5-c-label__text { display: flex; diff --git a/web/src/components/product/ProductSelectionPage.jsx b/web/src/components/product/ProductSelectionPage.jsx index 18b141cd69..a51894a833 100644 --- a/web/src/components/product/ProductSelectionPage.jsx +++ b/web/src/components/product/ProductSelectionPage.jsx @@ -20,11 +20,12 @@ */ import React, { useState } from "react"; -import { Card, CardBody, Flex, Form, Grid, GridItem, Radio, Split, Stack } from "@patternfly/react-core"; +import { Card, CardBody, Flex, Form, Grid, GridItem, Radio, List, ListItem, Split, Stack, FormGroup } from "@patternfly/react-core"; import { Page } from "~/components/core"; import { Center } from "~/components/layout"; import { useConfigMutation, useProduct } from "~/queries/software"; import styles from "@patternfly/react-styles/css/utilities/Text/text"; +import { slugify } from "~/utils"; import { sprintf } from "sprintf-js"; import { _ } from "~/i18n"; @@ -35,32 +36,35 @@ const ResponsiveGridItem = ({ children }) => ( ); const Option = ({ product, isChecked, onChange }) => { + const id = slugify(product.name); + const detailsId = `${id}-details`; const logoSrc = `assets/logos/${product.icon}`; // TRANSLATORS: %s will be replaced by a product name. E.g., "openSUSE Tumbleweed" const logoAltText = sprintf(_("%s logo"), product.name); return ( - - + + - - {logoAltText} - - {product.name} -

{product.description}

-
- - } - isChecked={isChecked} - onChange={onChange} - /> + + + {logoAltText} + + +

{product.description}

+
+
-
+ ); }; @@ -86,14 +90,20 @@ function ProductSelectionPage() {
- {products.map((product, index) => ( -