Skip to content

Commit 60d58dc

Browse files
committed
feat: add search in add connection modal
1 parent 286b9f8 commit 60d58dc

1 file changed

Lines changed: 61 additions & 4 deletions

File tree

client/src/features/connectedServices/ConnectedServicesPage.tsx

Lines changed: 61 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import {
2323
useContext,
2424
useEffect,
2525
useMemo,
26+
useRef,
2627
useState,
2728
type ReactNode,
2829
} from "react";
@@ -33,6 +34,7 @@ import {
3334
Plugin,
3435
PlusLg,
3536
Send,
37+
XCircleFill,
3638
XLg,
3739
} from "react-bootstrap-icons";
3840
import { Link, useSearchParams } from "react-router";
@@ -44,6 +46,9 @@ import {
4446
CardHeader,
4547
CardText,
4648
Col,
49+
Input,
50+
InputGroup,
51+
Label,
4752
ListGroup,
4853
ListGroupItem,
4954
Modal,
@@ -465,16 +470,38 @@ function AddIntegrationModal({
465470
}: AddIntegrationModalProps) {
466471
const [showAllIntegrations, setShowAllIntegrations] = useState(false);
467472
const isListExpanded = isOpen && showAllIntegrations;
473+
const searchInputRef = useRef<HTMLInputElement | null>(null);
474+
const [userSearchInput, setUserSearchInput] = useState("");
475+
476+
const normalizedSearchInput = useMemo(
477+
() => userSearchInput.trim().toLowerCase(),
478+
[userSearchInput]
479+
);
480+
481+
const filteredProviders = useMemo(() => {
482+
if (!normalizedSearchInput) return providers;
483+
484+
return providers.filter(({ provider }) =>
485+
[provider.display_name, provider.url].some((value) =>
486+
value.toLowerCase().includes(normalizedSearchInput)
487+
)
488+
);
489+
}, [normalizedSearchInput, providers]);
468490

469491
const visibleProviders = useMemo(() => {
470-
if (isListExpanded) return providers;
471-
return providers.slice(0, DEFAULT_MODAL_PROVIDERS_COUNT);
472-
}, [isListExpanded, providers]);
492+
if (isListExpanded) return filteredProviders;
493+
return filteredProviders.slice(0, DEFAULT_MODAL_PROVIDERS_COUNT);
494+
}, [filteredProviders, isListExpanded]);
473495

474496
const handleClosed = useCallback(() => {
475497
setShowAllIntegrations(false);
476498
}, []);
477499

500+
const onResetSearch = useCallback(() => {
501+
setUserSearchInput("");
502+
searchInputRef.current?.focus();
503+
}, []);
504+
478505
return (
479506
<Modal
480507
centered
@@ -495,6 +522,36 @@ function AddIntegrationModal({
495522
) : (
496523
<>
497524
<p>Add a new code, data, or compute integration.</p>
525+
<div className="mb-4">
526+
<Label for="search">Search</Label>
527+
<InputGroup>
528+
<Input
529+
className="lg"
530+
data-cy="add-integration-search-input"
531+
id="search"
532+
innerRef={searchInputRef}
533+
placeholder="Search by name or url"
534+
type="text"
535+
value={userSearchInput}
536+
onChange={(e) => setUserSearchInput(e.target.value)}
537+
onKeyDown={(e) => {
538+
if (e.key === "Enter") {
539+
e.preventDefault();
540+
}
541+
}}
542+
/>
543+
<Button
544+
color="outline-secondary"
545+
className="border-secondary-subtle"
546+
data-cy="search-clear-button"
547+
onClick={onResetSearch}
548+
id="search-button"
549+
type="button"
550+
>
551+
<XCircleFill className={cx("bi")} />
552+
</Button>
553+
</InputGroup>
554+
</div>
498555
<ListGroup
499556
className={cx(
500557
"bg-white",
@@ -521,7 +578,7 @@ function AddIntegrationModal({
521578
</div>
522579
</ListGroupItem>
523580
))}
524-
{providers.length > DEFAULT_MODAL_PROVIDERS_COUNT && (
581+
{filteredProviders.length > DEFAULT_MODAL_PROVIDERS_COUNT && (
525582
<button
526583
onClick={() => setShowAllIntegrations(!showAllIntegrations)}
527584
className={cx(

0 commit comments

Comments
 (0)