Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 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
2 changes: 1 addition & 1 deletion packages/connect-react/examples/nextjs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
},
"dependencies": {
"@pipedream/connect-react": "file:../..",
"@pipedream/sdk": "^1.3.3",
"@pipedream/sdk": "^1.8.0",
"next": "15.0.3",
"react": "19.0.0-rc-66855b96-20241106",
"react-dom": "19.0.0-rc-66855b96-20241106"
Expand Down
12 changes: 8 additions & 4 deletions packages/connect-react/examples/nextjs/src/app/page.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
"use client";

import { useState } from "react";
import { createFrontendClient } from "@pipedream/sdk/browser";
import {
ComponentFormContainer, FrontendClientProvider,
} from "@pipedream/connect-react";
import {
createFrontendClient,
type ConfigurableProp,
type ConfiguredProps,
} from "@pipedream/sdk/browser";
import { useState } from "react";
import { fetchToken } from "./actions";

export default function Home() {
Expand All @@ -16,7 +20,7 @@ export default function Home() {
const [
configuredProps,
setConfiguredProps,
] = useState({
] = useState<ConfiguredProps<ConfigurableProp[]>>({
text: "hello slack!",
});

Expand All @@ -28,7 +32,7 @@ export default function Home() {
const [
sdkResponse,
setSdkResponse,
] = useState<unknown | undefined>(undefined);
] = useState<ConfiguredProps<ConfigurableProp[]> | undefined>(undefined);

const handleDynamicProps = (dynamicProps: { id: string | undefined }) => {
setDynamicPropsId(dynamicProps.id)
Expand Down
4 changes: 2 additions & 2 deletions packages/connect-react/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@pipedream/connect-react",
"version": "1.5.0",
"version": "1.6.0",
"description": "Pipedream Connect library for React",
"files": [
"dist"
Expand Down Expand Up @@ -28,7 +28,7 @@
"author": "Pipedream Engineering",
"license": "MIT",
"dependencies": {
"@pipedream/sdk": "workspace:^",
"@pipedream/sdk": "^1.8.0",
"@tanstack/react-query": "^5.59.16",
"lodash.isequal": "^4.5.0",
"react-markdown": "^9.0.1",
Expand Down
12 changes: 6 additions & 6 deletions packages/connect-react/src/components/ComponentForm.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import {
DynamicProps,
FormContextProvider, type FormContext,
} from "../hooks/form-context";
import { DynamicProps } from "../types";
import type {
ConfigurableProps,
ConfiguredProps,
Expand All @@ -25,9 +25,9 @@ export type ComponentFormProps<T extends ConfigurableProps, U = ConfiguredProps<
propNames?: string[]; // TODO PropNames<T>
onSubmit?: (ctx: FormContext<T>) => void | Promise<void>; // if passed, we include button
onUpdateConfiguredProps?: (v: U) => void; // XXX onChange?
onUpdateDynamicProps?: (dp: DynamicProps<T>) => void;
onUpdateDynamicProps?: (dp: DynamicProps) => void;
hideOptionalProps?: boolean;
sdkResponse?: unknown | undefined;
sdkResponse?: U;
enableDebugging?: boolean;
/**
* OAuth app ID configuration for specific apps.
Expand All @@ -36,9 +36,9 @@ export type ComponentFormProps<T extends ConfigurableProps, U = ConfiguredProps<
*/
oauthAppConfig?: Record<string, string>;
} & (
| { externalUserId: string; userId?: never }
| { userId: string; externalUserId?: never }
);
| { externalUserId: string; userId?: never }
| { userId: string; externalUserId?: never }
);

export function ComponentForm<T extends ConfigurableProps>(props: ComponentFormProps<T>) {
return (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,11 @@ import {
// load a component and pass it down
type ComponentFormContainerProps<T extends ConfigurableProps> = Omit<ComponentFormProps<T>, "component"> & {
componentKey: string;
};
} & (
// Either externalUserId or userId must be provided
| { externalUserId: string; userId?: never }
| { userId: string; externalUserId?: never }
);

export function ComponentFormContainer<T extends ConfigurableProps>(props: ComponentFormContainerProps<T>) {
const {
Expand Down
12 changes: 4 additions & 8 deletions packages/connect-react/src/components/Control.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ import { ControlObject } from "./ControlObject";
import { ControlSelect } from "./ControlSelect";
import { ControlSql } from "./ControlSql";
import { RemoteOptionsContainer } from "./RemoteOptionsContainer";
import { sanitizeOption } from "../utils/type-guards";
import { LabelValueOption } from "../types";

export type ControlProps<T extends ConfigurableProps, U extends ConfigurableProp> = {
field: FormFieldContext<U>;
Expand All @@ -37,16 +39,10 @@ export function Control<T extends ConfigurableProps, U extends ConfigurableProp>
}

if ("options" in prop && prop.options) {
let options = prop.options;
if (typeof options[0] !== "object") {
options = options.map((o: unknown) => ({
label: o,
value: o,
}));
}
const options: LabelValueOption<any>[] = prop.options.map(sanitizeOption);
return <ControlSelect options={options} components={{
IndicatorSeparator: () => null,
}} />; // TODO fix typing issue here!
}} />;
}

// TODO just look at registry component repo and look for what we should make sure we support
Expand Down
62 changes: 38 additions & 24 deletions packages/connect-react/src/components/ControlApp.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,12 @@ import { useMemo } from "react";
import type { CSSProperties } from "react";
import type { OptionProps } from "react-select";
import type {
AppResponse, ConfigurablePropApp,
} from "@pipedream/sdk";
Account,
App,
ConfigurablePropApp,
} from "@pipedream/sdk/browser";

const BaseOption = (props: OptionProps<AppResponse>) => {
const BaseOption = (props: OptionProps<SelectValue>) => {
// const imgSrc =
// props.data.img_src ?? `https://pipedream.com/s.v0/${props.data.id}/logo/48`
return (
Expand All @@ -23,8 +25,14 @@ const BaseOption = (props: OptionProps<AppResponse>) => {
);
};

type AccountPlaceholder = {
id: "_new";
name: string;
}
type SelectValue = Account | AccountPlaceholder;

type ControlAppProps = {
app: AppResponse;
app: App;
};

export function ControlApp({ app }: ControlAppProps) {
Expand Down Expand Up @@ -55,7 +63,7 @@ export function ControlApp({ app }: ControlAppProps) {
gridArea: "control",
};

const baseSelectProps: BaseReactSelectProps = {
const baseSelectProps: BaseReactSelectProps<SelectValue> = {
components: {
Option: BaseOption,
},
Expand All @@ -67,7 +75,7 @@ export function ControlApp({ app }: ControlAppProps) {
}),
},
};
const selectProps = select.getProps("controlAppSelect", baseSelectProps);
const selectProps = select.getProps("controlAppSelect", baseSelectProps);

const oauthAppId = oauthAppConfig?.[app.name_slug];
const {
Expand All @@ -77,13 +85,16 @@ export function ControlApp({ app }: ControlAppProps) {
refetch: refetchAccounts,
} = useAccounts(
{
externalUserId,
external_user_id: externalUserId,
app: app.name_slug,
oauth_app_id: oauthAppId,
},
{
useQueryOpts: {
enabled: !!app,

// this seems to work (this overrides enabled so don't just set to true)
// @ts-ignore
suspense: !!app,
},
},
Expand All @@ -105,17 +116,26 @@ export function ControlApp({ app }: ControlAppProps) {
});
};

const selectValue = useMemo(() => {
let ret = value;
if (ret != null) {
const newAccountPlaceholder: AccountPlaceholder = {
id: "_new",
name: `Connect new ${app.name} account...`,
};

const selectOptions = useMemo<SelectValue[]>(() => [
...accounts,
newAccountPlaceholder,
], [accounts]);

const selectValue = useMemo<SelectValue>(() => {
if (value?.authProvisionId) {
for (const item of accounts) {
if (ret.authProvisionId === item.id) {
ret = item;
break;
if (value.authProvisionId === item.id) {
return item;
}
}
}
return ret;

return newAccountPlaceholder;
}, [
accounts,
value,
Expand All @@ -133,13 +153,7 @@ export function ControlApp({ app }: ControlAppProps) {
<Select
instanceId={id}
value={selectValue}
options={[
...accounts,
{
id: "_new",
name: `Connect new ${app.name} account...`,
},
]}
options={selectOptions}
{...selectProps}
required={true}
placeholder={`Select ${app.name} account...`}
Expand All @@ -151,8 +165,8 @@ export function ControlApp({ app }: ControlAppProps) {
onChange={(a) => {
if (a) {
if (a.id === "_new") {
// start connect account and then select it, etc.
// TODO unset / put in loading state
// start connect account and then select it, etc.
// TODO unset / put in loading state
startConnectAccount();
} else {
onChange({
Expand All @@ -169,7 +183,7 @@ export function ControlApp({ app }: ControlAppProps) {
app,
...formFieldCtx,
})} onClick={() => startConnectAccount()}>
Connect {app.name}
Connect {app.name}
</button>
}
</div>
Expand Down
Loading
Loading