Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/sidebar-integration-connect.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@executor-js/react": patch
---

Add a shortcut beside the sidebar integration list that opens the existing integration picker.
8 changes: 5 additions & 3 deletions e2e/scenarios/provider-plugins-ui.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,12 @@ scenario(
const identity = yield* target.newIdentity();

yield* browser.session(identity, async ({ page, step }) => {
await step("Open the integrations picker", async () => {
await visit(page, "/integrations");
await step("Open the integrations picker from the sidebar", async () => {
await visit(page, "/policies");
await clickToReveal(
page.getByRole("button", { name: "Connect" }),
page
.getByRole("navigation")
.getByRole("button", { name: "Connect an integration", exact: true }),
page.getByRole("dialog", { name: "Connect an integration" }),
);
});
Expand Down
28 changes: 26 additions & 2 deletions packages/react/src/multiplayer/shell.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { Link, Outlet, useLocation, useParams } from "@tanstack/react-router";
import { useEffect, useRef, useState, type ReactNode } from "react";
import { useAtomValue } from "@effect/atom-react";
import * as AsyncResult from "effect/unstable/reactivity/AsyncResult";
import { BookOpen, Command, ExternalLink } from "lucide-react";
import { BookOpen, Command, ExternalLink, PlusIcon } from "lucide-react";
import type { Integration } from "@executor-js/sdk/shared";
import { integrationsOptimisticAtom } from "../api/atoms";
import { trackEvent } from "../api/analytics";
Expand All @@ -23,6 +23,7 @@ import {
} from "../components/integration-favicon";
import { CommandPalette } from "../components/command-palette";
import { Wordmark } from "../components/wordmark";
import { ConnectDialog } from "../pages/integrations";
import { useClientPlugins, useIntegrationPlugins } from "@executor-js/sdk/client";
import { useAuth } from "./auth-context";

Expand Down Expand Up @@ -347,6 +348,7 @@ function SidebarContent(
onNavigate?: () => void;
showBrand?: boolean;
onOpenCommands: () => void;
onOpenIntegrationConnect: () => void;
},
) {
const plugins = useClientPlugins();
Expand Down Expand Up @@ -382,8 +384,19 @@ function SidebarContent(
/>
))}

<div className="mt-5 mb-1 px-2.5 text-xs font-medium uppercase tracking-widest text-muted-foreground">
<div className="mt-5 mb-1 flex items-center justify-between px-2.5 text-xs font-medium uppercase tracking-widest text-muted-foreground">
<span>Integrations</span>
<Button
type="button"
variant="ghost"
size="icon-xs"
aria-label="Connect an integration"
title="Connect an integration"
onClick={props.onOpenIntegrationConnect}
className="-my-1 text-muted-foreground hover:bg-sidebar-active/60 hover:text-foreground"
>
<PlusIcon className="size-3.5" />
</Button>
</div>

<IntegrationList pathname={props.pathname} onNavigate={props.onNavigate} />
Expand Down Expand Up @@ -415,6 +428,7 @@ export function Shell(props: ShellProps) {
const lastPathname = useRef(pathname);
const [mobileSidebarOpen, setMobileSidebarOpen] = useState(false);
const [commandPaletteOpen, setCommandPaletteOpen] = useState(false);
const [connectIntegrationOpen, setConnectIntegrationOpen] = useState(false);
if (lastPathname.current !== pathname) {
lastPathname.current = pathname;
if (mobileSidebarOpen) setMobileSidebarOpen(false);
Expand All @@ -432,12 +446,17 @@ export function Shell(props: ShellProps) {
return (
<div className="flex h-screen overflow-hidden">
<CommandPalette open={commandPaletteOpen} onOpenChange={setCommandPaletteOpen} />
<ConnectDialog open={connectIntegrationOpen} onOpenChange={setConnectIntegrationOpen} />
{/* Desktop sidebar */}
<aside className="hidden w-52 shrink-0 border-r border-sidebar-border bg-sidebar md:flex md:flex-col lg:w-56">
<SidebarContent
{...props}
pathname={pathname}
onOpenCommands={() => setCommandPaletteOpen(true)}
onOpenIntegrationConnect={() => {
setConnectIntegrationOpen(true);
trackEvent("integration_connect_dialog_opened");
}}
/>
</aside>

Expand Down Expand Up @@ -481,6 +500,11 @@ export function Shell(props: ShellProps) {
setMobileSidebarOpen(false);
setCommandPaletteOpen(true);
}}
onOpenIntegrationConnect={() => {
setMobileSidebarOpen(false);
setConnectIntegrationOpen(true);
trackEvent("integration_connect_dialog_opened");
}}
/>
</div>
</div>
Expand Down
2 changes: 1 addition & 1 deletion packages/react/src/pages/integrations.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ const looksLikeUrl = (raw: string): boolean => {
return false;
};

function ConnectDialog(props: { open: boolean; onOpenChange: (open: boolean) => void }) {
export function ConnectDialog(props: { open: boolean; onOpenChange: (open: boolean) => void }) {
const integrationPlugins = useIntegrationPlugins();
const doDetect = useAtomSet(detectIntegration, { mode: "promiseExit" });
const navigate = useNavigate();
Expand Down