From 87ce0a4c84b549467a4f3ca40739886d74c79702 Mon Sep 17 00:00:00 2001
From: myelinated-wackerow
<263208946+myelinated-wackerow@users.noreply.github.com>
Date: Fri, 8 May 2026 09:58:29 -0700
Subject: [PATCH] a11y: ProductTable filter sidebar semantics
Wraps shared Filter groups in
)
diff --git a/src/components/ProductTable/FilterInputs/SwitchFilterInput.tsx b/src/components/ProductTable/FilterInputs/SwitchFilterInput.tsx
index 55305775463..60939e75f0c 100644
--- a/src/components/ProductTable/FilterInputs/SwitchFilterInput.tsx
+++ b/src/components/ProductTable/FilterInputs/SwitchFilterInput.tsx
@@ -1,8 +1,11 @@
-import { ReactElement } from "react"
+"use client"
+
+import { ReactElement, useId } from "react"
import type { LucideIcon } from "lucide-react"
import { FilterInputState } from "@/lib/types"
+import { Field, FieldDescription, FieldLabel } from "@/components/ui/field"
import Switch from "@/components/ui/switch"
interface SwitchFilterInputProps {
@@ -28,26 +31,40 @@ const SwitchFilterInput = ({
inputState,
updateFilterState,
}: SwitchFilterInputProps) => {
+ const id = useId()
+ const descriptionId = description ? `${id}-description` : undefined
return (
- <>
-
-
-
+
+
+
+
{Icon && (
)}
-
- {label}
-
+
+ {label}
+
{
updateFilterState(filterIndex, itemIndex, e as boolean)
}}
/>
-
{description}
- >
+ {description && (
+
+ {description}
+
+ )}
+
)
}
diff --git a/src/components/ui/field.tsx b/src/components/ui/field.tsx
new file mode 100644
index 00000000000..64921f8f070
--- /dev/null
+++ b/src/components/ui/field.tsx
@@ -0,0 +1,243 @@
+import { useMemo } from "react"
+import { cva, type VariantProps } from "class-variance-authority"
+
+import { Label } from "@/components/ui/label"
+import { Separator } from "@/components/ui/separator"
+
+import { cn } from "@/lib/utils/cn"
+
+function FieldSet({ className, ...props }: React.ComponentProps<"fieldset">) {
+ return (
+
[data-slot=checkbox-group]]:gap-3 has-[>[data-slot=radio-group]]:gap-3",
+ className
+ )}
+ {...props}
+ />
+ )
+}
+
+function FieldLegend({
+ className,
+ variant = "legend",
+ ...props
+}: React.ComponentProps<"legend"> & { variant?: "legend" | "label" }) {
+ return (
+
+ )
+}
+
+function FieldGroup({ className, ...props }: React.ComponentProps<"div">) {
+ return (
+ [data-slot=field-group]]:gap-4",
+ className
+ )}
+ {...props}
+ />
+ )
+}
+
+const fieldVariants = cva(
+ "group/field data-[invalid=true]:text-error flex w-full gap-3",
+ {
+ variants: {
+ orientation: {
+ vertical: ["flex-col [&>*]:w-full [&>.sr-only]:w-auto"],
+ horizontal: [
+ "flex-row items-center",
+ "[&>[data-slot=field-label]]:flex-auto",
+ "has-[>[data-slot=field-content]]:[&>[role=checkbox],[role=radio]]:mt-px has-[>[data-slot=field-content]]:items-start",
+ ],
+ responsive: [
+ "@md/field-group:flex-row @md/field-group:items-center @md/field-group:[&>*]:w-auto flex-col [&>*]:w-full [&>.sr-only]:w-auto",
+ "@md/field-group:[&>[data-slot=field-label]]:flex-auto",
+ "@md/field-group:has-[>[data-slot=field-content]]:items-start @md/field-group:has-[>[data-slot=field-content]]:[&>[role=checkbox],[role=radio]]:mt-px",
+ ],
+ },
+ },
+ defaultVariants: {
+ orientation: "vertical",
+ },
+ }
+)
+
+function Field({
+ className,
+ orientation = "vertical",
+ ...props
+}: React.ComponentProps<"div"> & VariantProps
) {
+ return (
+
+ )
+}
+
+function FieldContent({ className, ...props }: React.ComponentProps<"div">) {
+ return (
+
+ )
+}
+
+function FieldLabel({
+ className,
+ ...props
+}: React.ComponentProps) {
+ return (
+