Skip to content

Commit

Permalink
refactor(web): rework product selector
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
dgdavid committed Sep 4, 2024
1 parent e85aef0 commit ec57cc1
Show file tree
Hide file tree
Showing 3 changed files with 42 additions and 28 deletions.
2 changes: 1 addition & 1 deletion web/src/assets/styles/app.scss
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ button.remove-link:hover {
height: 20px;
}

label {
.pf-v5-c-card {
img {
width: 80px;
}
Expand Down
4 changes: 4 additions & 0 deletions web/src/assets/styles/utilities.scss
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
64 changes: 37 additions & 27 deletions web/src/components/product/ProductSelectionPage.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -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";

Expand All @@ -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 (
<ResponsiveGridItem>
<Card isRounded>
<ListItem>
<Card isRounded onClick={onChange} className="cursor-pointer">
<CardBody>
<Radio
name="product"
id={product.name}
label={
<Split hasGutter>
<img src={logoSrc} alt={logoAltText} />
<Stack hasGutter>
<span className={`${styles.fontSizeLg} ${styles.fontWeightBold}`}>{product.name}</span>
<p>{product.description}</p>
</Stack>
</Split>
}
isChecked={isChecked}
onChange={onChange}
/>
<Split hasGutter>
<Radio
id={id}
name="product"
isChecked={isChecked}
onChange={onChange}
aria-details={detailsId}
/>
<img aria-hidden src={logoSrc} alt={logoAltText} />
<Stack hasGutter>
<label htmlFor={id} className={`${styles.fontSizeLg} ${styles.fontWeightBold}`}>
{product.name}
</label>
<p id={detailsId}>{product.description}</p>
</Stack>
</Split>
</CardBody>
</Card>
</ResponsiveGridItem>
</ListItem>
);
};

Expand All @@ -86,14 +90,20 @@ function ProductSelectionPage() {
<Center>
<Form id="productSelectionForm" onSubmit={onSubmit}>
<Grid hasGutter>
{products.map((product, index) => (
<Option
key={index}
product={product}
isChecked={nextProduct === product}
onChange={() => setNextProduct(product)}
/>
))}
<ResponsiveGridItem>
<List isPlain>
<FormGroup role="radiogroup" label={_("Select a product")}>
{products.map((product, index) => (
<Option
key={index}
product={product}
isChecked={nextProduct === product}
onChange={() => setNextProduct(product)}
/>
))}
</FormGroup>
</List>
</ResponsiveGridItem>
<ResponsiveGridItem>
<Flex justifyContent={{ default: "justifyContentFlexEnd" }}>
{selectedProduct && !isLoading && <Page.CancelAction navigateTo={-1} />}
Expand Down

0 comments on commit ec57cc1

Please sign in to comment.