diff --git a/web/src/assets/styles/blocks.scss b/web/src/assets/styles/blocks.scss
index e568ae24db..65fddee327 100644
--- a/web/src/assets/styles/blocks.scss
+++ b/web/src/assets/styles/blocks.scss
@@ -248,7 +248,7 @@ ul[data-type="agama/list"] {
li {
border: 2px solid var(--color-gray-dark);
- padding: var(--spacer-normal);
+ padding: var(--spacer-small);
text-align: start;
background: var(--color-gray-light);
margin-block-end: 0;
@@ -276,7 +276,6 @@ ul[data-type="agama/list"] {
// not belongs to a listbox or grid list ul.
li[aria-selected] {
background: var(--color-gray-dark);
- font-weight: 700;
&:not(:last-child) {
border-bottom-color: white;
@@ -314,15 +313,33 @@ ul[data-type="agama/list"][role="grid"] {
div[role="gridcell"] {
display: flex;
align-items: center;
- gap: var(--spacer-normal);
+ gap: var(--spacer-small);
- & > input {
+ input {
--size: var(--fs-h2);
+ cursor: pointer;
block-size: var(--size);
inline-size: var(--size);
+
+ &[data-auto-selected] {
+ accent-color: white;
+ box-shadow: 0 0 1px;
+ }
+ }
+
+ & > div:first-child {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ gap: var(--spacer-small);
+
+ span {
+ font-size: var(--fs-small);
+ font-weight: bold;
+ }
}
- & > div {
+ & > div:last-child {
flex: 1;
}
}
@@ -386,6 +403,18 @@ ul[data-type="agama/list"][role="grid"] {
}
}
+ul[data-items-type="agama/patterns"] {
+ div[role="gridcell"] {
+ & > div:first-child {
+ min-width: 65px;
+ }
+
+ & > div:last-child * {
+ margin-block-end: var(--spacer-small);
+ }
+ }
+}
+
[role="dialog"] {
.sticky-top-0 {
position: sticky;
diff --git a/web/src/components/core/Selector.jsx b/web/src/components/core/Selector.jsx
index 383fc55a16..5e5763e6e1 100644
--- a/web/src/components/core/Selector.jsx
+++ b/web/src/components/core/Selector.jsx
@@ -21,6 +21,7 @@
// @ts-check
import React from "react";
+import { _ } from "~/i18n";
import { noop } from "~/utils";
/**
@@ -61,6 +62,7 @@ import { noop } from "~/utils";
* @param {function} props.renderOption=noop - Function used for rendering options.
* @param {string} [props.optionIdKey="id"] - Key used for retrieve options id.
* @param {Array<*>} [props.selectedIds=[]] - Identifiers for selected options.
+ * @param {function} props.autoSelectionCheck=noop - Function used to check if option should be marked as auto selected.
* @param {onSelectionChangeCallback} [props.onSelectionChange=noop] - Callback to be called when the selection changes.
* @param {function|undefined} [props.onOptionClick] - Callback to be called when the selection changes.
* @param {object} [props.props] - Other props sent to the internal selector
component
@@ -72,6 +74,7 @@ const Selector = ({
renderOption = noop,
optionIdKey = "id",
selectedIds = [],
+ autoSelectionCheck = noop,
onSelectionChange = noop,
onOptionClick,
...props
@@ -99,6 +102,7 @@ const Selector = ({
const optionId = option[optionIdKey];
const optionHtmlId = `${id}-option-${optionId}`;
const isSelected = selectedIds.includes(optionId);
+ const isAutoSelected = isSelected && autoSelectionCheck(option);
const onClick = () => onOptionClicked(optionId);
return (
@@ -108,14 +112,19 @@ const Selector = ({
role="row"
onClick={onClick}
aria-selected={isSelected || undefined}
+ data-auto-selected={isAutoSelected || undefined}
>
-
+
+
+ {isAutoSelected &&
{_("auto selected")}
}
+
{renderOption(option)}
diff --git a/web/src/components/core/Selector.test.jsx b/web/src/components/core/Selector.test.jsx
index 0693d790e4..96fa778cf9 100644
--- a/web/src/components/core/Selector.test.jsx
+++ b/web/src/components/core/Selector.test.jsx
@@ -71,6 +71,21 @@ describe("Selector", () => {
expect(onChangeFn).toHaveBeenCalledWith([2]);
});
+ it("sets data-auto-selected attribute to selected options for which `autoSelectionCheck` returns true", () => {
+ plainRender(
+ // Forcing a not selected option (en_GB) as auto selected for checking that it is not.
+ ["es_ES", "en_GB"].includes(o.id)} />
+ );
+ const spanishRow = screen.getByRole("row", { name: "Spanish - Spain auto selected" });
+ const englishRow = screen.getByRole("row", { name: "English - United Kingdom" });
+ const spanishOption = within(spanishRow).getByRole("radio");
+ const englishOption = within(englishRow).getByRole("radio");
+ expect(spanishRow).toHaveAttribute("data-auto-selected");
+ expect(spanishOption).toHaveAttribute("data-auto-selected");
+ expect(englishRow).not.toHaveAttribute("data-auto-selected");
+ expect(englishOption).not.toHaveAttribute("data-auto-selected");
+ });
+
describe("when set as single selector", () => {
it("renders a radio input for each option", () => {
plainRender();
diff --git a/web/src/components/software/PatternSelector.jsx b/web/src/components/software/PatternSelector.jsx
index ceedc238fd..3fe6b5cca7 100644
--- a/web/src/components/software/PatternSelector.jsx
+++ b/web/src/components/software/PatternSelector.jsx
@@ -135,12 +135,12 @@ function PatternSelector({ patterns, onSelectionChanged = noop }) {
const groups = groupPatterns(visiblePatterns);
const renderPatternOption = (pattern) => (
- <>
+
{pattern.summary}
{pattern.description}
- >
+
);
const selector = sortGroups(groups).map((groupName) => {
@@ -159,6 +159,8 @@ function PatternSelector({ patterns, onSelectionChanged = noop }) {
onOptionClick={onToggle}
optionIdKey="name"
selectedIds={selectedIds}
+ autoSelectionCheck={pattern => pattern.selectedBy === SelectedBy.AUTO}
+ data-items-type="agama/patterns"
/>
);