Skip to content

Commit 324f594

Browse files
committed
chore: extract combobox component
1 parent e61b2dc commit 324f594

File tree

2 files changed

+108
-70
lines changed

2 files changed

+108
-70
lines changed
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
import { Button } from "components/Button/Button";
2+
import {
3+
Command,
4+
CommandEmpty,
5+
CommandGroup,
6+
CommandInput,
7+
CommandItem,
8+
CommandList,
9+
} from "components/Command/Command";
10+
import {
11+
Popover,
12+
PopoverContent,
13+
PopoverTrigger,
14+
} from "components/Popover/Popover";
15+
import { Check, ChevronDown, CornerDownLeft } from "lucide-react";
16+
import type { FC, KeyboardEventHandler } from "react";
17+
import { cn } from "utils/cn";
18+
19+
interface ComboboxProps {
20+
value: string;
21+
options?: string[];
22+
placeholder?: string;
23+
open: boolean;
24+
onOpenChange: (open: boolean) => void;
25+
inputValue: string;
26+
onInputChange: (value: string) => void;
27+
onKeyDown?: KeyboardEventHandler<HTMLInputElement>;
28+
onSelect: (value: string) => void;
29+
}
30+
31+
export const Combobox: FC<ComboboxProps> = ({
32+
value,
33+
options = [],
34+
placeholder = "Select option",
35+
open,
36+
onOpenChange,
37+
inputValue,
38+
onInputChange,
39+
onKeyDown,
40+
onSelect,
41+
}) => {
42+
return (
43+
<Popover open={open} onOpenChange={onOpenChange}>
44+
<PopoverTrigger asChild>
45+
<Button
46+
variant="outline"
47+
aria-expanded={open}
48+
className="w-72 justify-between group"
49+
>
50+
<span className={cn(!value && "text-content-secondary")}>
51+
{value || placeholder}
52+
</span>
53+
<ChevronDown className="size-icon-sm text-content-secondary group-hover:text-content-primary" />
54+
</Button>
55+
</PopoverTrigger>
56+
<PopoverContent className="w-72">
57+
<Command>
58+
<CommandInput
59+
placeholder="Search or enter custom value"
60+
value={inputValue}
61+
onValueChange={onInputChange}
62+
onKeyDown={onKeyDown}
63+
/>
64+
<CommandList>
65+
<CommandEmpty>
66+
<p>No results found</p>
67+
<span className="flex flex-row items-center justify-center gap-1">
68+
Enter custom value
69+
<CornerDownLeft className="size-icon-sm bg-surface-tertiary rounded-sm p-1" />
70+
</span>
71+
</CommandEmpty>
72+
<CommandGroup>
73+
{options.map((option) => (
74+
<CommandItem
75+
key={option}
76+
value={option}
77+
onSelect={(currentValue) => {
78+
onSelect(currentValue === value ? "" : currentValue);
79+
}}
80+
>
81+
{option}
82+
{value === option && (
83+
<Check className="size-icon-sm ml-auto" />
84+
)}
85+
</CommandItem>
86+
))}
87+
</CommandGroup>
88+
</CommandList>
89+
</Command>
90+
</PopoverContent>
91+
</Popover>
92+
);
93+
};

site/src/pages/DeploymentSettingsPage/IdpOrgSyncPage/IdpOrgSyncPageView.tsx

Lines changed: 15 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,7 @@ import type {
44
} from "api/typesGenerated";
55
import { ErrorAlert } from "components/Alert/ErrorAlert";
66
import { Button } from "components/Button/Button";
7-
import {
8-
Command,
9-
CommandEmpty,
10-
CommandGroup,
11-
CommandInput,
12-
CommandItem,
13-
CommandList,
14-
} from "components/Command/Command";
7+
import { Combobox } from "components/Combobox/Combobox";
158
import { ChooseOne, Cond } from "components/Conditionals/ChooseOne";
169
import {
1710
Dialog,
@@ -227,68 +220,20 @@ export const IdpOrgSyncPageView: FC<IdpSyncPageViewProps> = ({
227220
</Label>
228221

229222
{claimFieldValues ? (
230-
<Popover open={open} onOpenChange={setOpen}>
231-
<PopoverTrigger asChild>
232-
<Button
233-
variant="outline"
234-
aria-expanded={open}
235-
className="w-72 justify-between"
236-
>
237-
<span
238-
className={cn(
239-
!idpOrgName && "text-content-secondary",
240-
)}
241-
>
242-
{idpOrgName || "Select IdP organization"}
243-
</span>
244-
<ChevronDown className="size-icon-sm cursor-pointer text-content-secondary hover:text-content-primary" />
245-
</Button>
246-
</PopoverTrigger>
247-
<PopoverContent className="w-72">
248-
<Command>
249-
<CommandInput
250-
placeholder="Search or enter custom value"
251-
value={inputValue}
252-
onValueChange={setInputValue}
253-
onKeyDown={handleKeyDown}
254-
/>
255-
<CommandList>
256-
<CommandEmpty>
257-
<p>No results found</p>
258-
<span className="flex flex-row items-center justify-center gap-1">
259-
Enter custom value
260-
<CornerDownLeft className="size-icon-sm bg-surface-tertiary rounded-sm p-1" />
261-
</span>
262-
</CommandEmpty>
263-
<CommandGroup>
264-
{claimFieldValues.map((value) => (
265-
<CommandItem
266-
key={value}
267-
value={value}
268-
onSelect={(currentValue) => {
269-
setIdpOrgName(
270-
currentValue === idpOrgName
271-
? ""
272-
: currentValue,
273-
);
274-
setOpen(false);
275-
}}
276-
>
277-
{value}
278-
{idpOrgName === value && (
279-
<Check
280-
size={16}
281-
strokeWidth={2}
282-
className="ml-auto"
283-
/>
284-
)}
285-
</CommandItem>
286-
))}
287-
</CommandGroup>
288-
</CommandList>
289-
</Command>
290-
</PopoverContent>
291-
</Popover>
223+
<Combobox
224+
value={idpOrgName}
225+
options={claimFieldValues}
226+
placeholder="Select IdP organization"
227+
open={open}
228+
onOpenChange={setOpen}
229+
inputValue={inputValue}
230+
onInputChange={setInputValue}
231+
onKeyDown={handleKeyDown}
232+
onSelect={(value: string) => {
233+
setIdpOrgName(value);
234+
setOpen(false);
235+
}}
236+
/>
292237
) : (
293238
<Input
294239
id={`${id}-idp-org-name`}

0 commit comments

Comments
 (0)