Skip to content

Commit 9b9e117

Browse files
atrakhConvex, Inc.
authored andcommitted
dashboard: make comboboxes focusable (#35101)
- Work around headlessui to make sure the combobox button has tabIndex set to 0 - Improve focus styles on button and tooltip elements - Improve some border styling in the filter controls GitOrigin-RevId: 4a7d1ac3440364744a60363bc5ecc174784542a4
1 parent eb9704a commit 9b9e117

File tree

7 files changed

+36
-19
lines changed

7 files changed

+36
-19
lines changed

npm-packages/dashboard-common/src/elements/Button.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ export const Button = forwardRef<HTMLElement, ButtonProps>(function Button(
103103
});
104104

105105
const button = tv({
106-
base: "inline-flex animate-fadeInFromLoading select-none items-center whitespace-nowrap rounded text-sm font-medium transition-colors focus-visible:outline-0 focus-visible:ring-1 focus-visible:ring-util-accent/40 focus-visible:ring-offset-2",
106+
base: "inline-flex animate-fadeInFromLoading select-none items-center whitespace-nowrap rounded text-sm font-medium transition-colors focus-visible:border focus-visible:border-border-selected focus-visible:outline-none",
107107
variants: {
108108
variant: {
109109
primary:

npm-packages/dashboard-common/src/elements/Combobox.tsx

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ export function Combobox<T>({
5353
disabled?: boolean;
5454
unknownLabel?: (value: T) => string;
5555
processFilterOption?: (option: string) => string;
56-
size?: "xs" | "sm" | "md";
56+
size?: "sm" | "md";
5757
icon?: React.ReactNode;
5858
}) {
5959
const [query, setQuery] = useState("");
@@ -62,6 +62,14 @@ export function Combobox<T>({
6262
const [popperElement, setPopperElement] = useState<HTMLDivElement | null>(
6363
null,
6464
);
65+
66+
// Force tabindex to 0
67+
useEffect(() => {
68+
if (referenceElement?.children[0]) {
69+
(referenceElement.children[0] as HTMLElement).tabIndex = 0;
70+
}
71+
}, [referenceElement]);
72+
6573
const [isOpen, setIsOpen] = useState(false);
6674

6775
const { styles, attributes, update } = usePopper(
@@ -150,11 +158,10 @@ export function Combobox<T>({
150158
className={cn(
151159
"flex gap-1 w-full items-center group",
152160
"truncate relative text-left text-content-primary rounded disabled:bg-background-tertiary disabled:text-content-secondary disabled:cursor-not-allowed",
153-
"border focus:border-border-selected focus:outline-none bg-background-secondary text-sm",
161+
"border focus-visible:z-10 focus-visible:border-border-selected focus-visible:outline-none bg-background-secondary text-sm",
154162
"hover:bg-background-tertiary",
155163
"cursor-pointer",
156164
open && "border-border-selected z-10",
157-
size === "xs" && "py-0.25 px-0.25 text-xs",
158165
size === "sm" && "py-1 px-2 text-xs",
159166
size === "md" && "py-2 px-3",
160167
innerButtonClasses,

npm-packages/dashboard-common/src/elements/MultiSelectCombobox.tsx

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,14 @@ export function MultiSelectCombobox({
4242
const [popperElement, setPopperElement] = useState<HTMLDivElement | null>(
4343
null,
4444
);
45+
46+
// Force tabindex to 0
47+
useEffect(() => {
48+
if (referenceElement?.children[0]) {
49+
(referenceElement.children[0] as HTMLElement).tabIndex = 0;
50+
}
51+
}, [referenceElement]);
52+
4553
const [isOpen, setIsOpen] = useState(false);
4654

4755
const { styles, attributes, update } = usePopper(

npm-packages/dashboard-common/src/elements/Tooltip.tsx

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,10 +28,7 @@ export function Tooltip({
2828
<RadixTooltip.Root>
2929
<RadixTooltip.Trigger
3030
asChild={wrapsButton}
31-
className={classNames(
32-
"focus-visible:outline-0 focus-visible:ring-1 focus-visible:ring-util-accent/40 rounded focus-visible:ring-offset-2",
33-
className,
34-
)}
31+
className={classNames("focus-visible:outline-0", className)}
3532
>
3633
{children}
3734
</RadixTooltip.Trigger>

npm-packages/dashboard-common/src/features/data/components/DataFilters/DataFilters.tsx

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -107,17 +107,15 @@ export function DataFilters({
107107
<div className="flex flex-col">
108108
<div className="flex justify-between gap-2">
109109
<div className="flex items-center">
110-
<div
111-
className={cn(
112-
"flex w-full divide-x divide-border-transparent rounded border bg-background-secondary",
113-
showFilters && "rounded-b-none border-b-0",
114-
)}
115-
>
110+
<div className="flex w-full rounded bg-background-secondary">
116111
<div className="flex items-center">
117112
<Button
118113
size="xs"
119114
variant="neutral"
120-
className="rounded-r-none"
115+
className={cn(
116+
"rounded-r-none border border-r-0",
117+
showFilters && "rounded-b-none border-b-0",
118+
)}
121119
icon={<ArrowLeftIcon className="my-[1px]" />}
122120
inline
123121
tip="Previous Filters"
@@ -131,7 +129,10 @@ export function DataFilters({
131129
<Button
132130
size="xs"
133131
variant="neutral"
134-
className="rounded-none"
132+
className={cn(
133+
"rounded-none border border-x-0",
134+
showFilters && "border-b-0",
135+
)}
135136
icon={<ArrowRightIcon className="my-[1px]" />}
136137
tip="Next Filters"
137138
inline

npm-packages/dashboard-common/src/features/data/components/DataFilters/FilterButton.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import {
55
} from "@radix-ui/react-icons";
66
import { FilterExpression } from "system-udfs/convex/_system/frontend/lib/filters";
77
import { Button } from "@common/elements/Button";
8+
import { cn } from "@common/lib/cn";
89

910
export const filterMenuId = "filterMenu";
1011

@@ -49,7 +50,10 @@ export function FilterButton({
4950
aria-controls={filterMenuId}
5051
icon={<MixerHorizontalIcon className="size-3.5" />}
5152
focused={open}
52-
className="w-fit rounded-l-none text-xs"
53+
className={cn(
54+
"w-fit rounded-l-none text-xs border",
55+
open && "rounded-b-none border-b-0",
56+
)}
5357
inline
5458
>
5559
{filterButtonContent}

npm-packages/dashboard-common/src/features/data/components/FilterEditor/FilterEditor.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -192,7 +192,7 @@ function ValueEditor({
192192
state.field === "_creationTime" && typeof state.value === "number";
193193

194194
return (
195-
<div className="ml-[-1px] min-w-0 grow">
195+
<div className="ml-[-1px] min-w-0 grow focus-within:z-20">
196196
{isTypeFilterOp(state.op) ? (
197197
<Combobox
198198
searchPlaceholder="Search types..."
@@ -221,7 +221,7 @@ function ValueEditor({
221221
) : (
222222
<ObjectEditor
223223
key={objectEditorKey}
224-
className="min-w-4 rounded-none border"
224+
className="min-w-4 rounded-none border focus-within:border focus-within:border-border-selected"
225225
editorClassname="px-2 py-1 mt-0 rounded bg-background-secondary rounded-l-none rounded-r-none"
226226
size="sm"
227227
disableFolding

0 commit comments

Comments
 (0)