From f1de80477a8531caec0f25af3b6b7cc1958ed4c8 Mon Sep 17 00:00:00 2001 From: m5r Date: Sat, 29 Aug 2026 12:33:00 +0200 Subject: [PATCH] admin: group sources and safe search become rac checkboxes the assigned sources list is a rac checkboxgroup and safe search uses the same drawn checkbox, extracted to a shared ui component with grouped and standalone modes enforced by a discriminated union. the label carries a 44px pointer-target floor on both axes, the focus ring is driven from rac's focus-visible state and guarded by a test, and toggleSource is gone because the group hands back the whole set. --- .../configuration/GroupSourcesEditor.tsx | 41 +++--- .../configuration/ProtectionGroups.tsx | 30 ++-- .../configuration/ProtectionPage.test.tsx | 23 +++ .../features/configuration/sourceSet.test.ts | 16 +- admin/src/features/configuration/sourceSet.ts | 5 - admin/src/ui/Checkbox.test.tsx | 92 ++++++++++++ admin/src/ui/Checkbox.tsx | 138 ++++++++++++++++++ 7 files changed, 281 insertions(+), 64 deletions(-) create mode 100644 admin/src/ui/Checkbox.test.tsx create mode 100644 admin/src/ui/Checkbox.tsx diff --git a/admin/src/features/configuration/GroupSourcesEditor.tsx b/admin/src/features/configuration/GroupSourcesEditor.tsx index a0a5291..c13315c 100644 --- a/admin/src/features/configuration/GroupSourcesEditor.tsx +++ b/admin/src/features/configuration/GroupSourcesEditor.tsx @@ -3,7 +3,8 @@ import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; import * as stylex from "@stylexjs/stylex"; import { groupSourcesPutMutation, groupSourcesQuery } from "@/lib/queries"; import type { Blocklist } from "@/lib/types"; -import { sameSet, toggleSource } from "./sourceSet"; +import { sameSet } from "./sourceSet"; +import Checkbox, { CheckboxGroup } from "@/ui/Checkbox"; import InlineError from "@/lib/InlineError"; import { styles as shared } from "@/ui/styles"; import { colors } from "@/ui/tokens.stylex"; @@ -28,13 +29,6 @@ const styles = stylex.create({ flexDirection: "column", gap: "0.25rem", }, - checkboxLabel: { - display: "inline-flex", - alignItems: "center", - gap: "0.5rem", - fontSize: "0.875rem", - lineHeight: "1.25rem", - }, buttonRow: { marginTop: "0.75rem", display: "flex", @@ -66,21 +60,22 @@ export default function GroupSourcesEditor({ groupId, blocklists }: Props) { return (
- + {/* The section's own "Assigned sources" heading is the visible label; a + Label here would put the same words on screen twice. Ids cross the + React Aria boundary as strings, the same convention as Select. */} + setSelected(values.map(Number).sort((a, b) => a - b))} + > +
    + {blocklists.map((blocklist) => ( +
  • + {blocklist.name} +
  • + ))} +
+