Skip to content
Merged
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
2 changes: 1 addition & 1 deletion components/apex/apex.app.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,4 @@ export default {
console.log(Object.keys(this.$auth));
},
},
};
};
2 changes: 1 addition & 1 deletion components/openthesaurus/openthesaurus.app.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,4 @@ export default {
console.log(Object.keys(this.$auth));
},
},
};
};
14 changes: 14 additions & 0 deletions packages/connect-react/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,20 @@

# Changelog

## [2.0.0] - 2025-10-02

### Breaking Changes

Use the new v2 version of the Pipedream SDK (i.e. `@pipedreamhq/pipedream-sdk`).
This change involves migrating to the new types mostly, but also runtime changes
involving the API calls.

The runtime behavior should not be affected from a user's perspective, except
for consumers of the `connect-react` package itself, since some components (e.g.
`FrontendClientProvider`) expect their consumers to inject a client instance of
the same SDK version. For this reason, this change bumps the **major** version
of this package.

## [1.5.0] - 2025-08-18

### Fixed
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.6.0",
"version": "2.0.0",
"description": "Pipedream Connect library for React",
"files": [
"dist"
Expand Down Expand Up @@ -30,7 +30,7 @@
"author": "Pipedream Engineering",
"license": "MIT",
"dependencies": {
"@pipedream/sdk": "^1.8.0",
"@pipedream/sdk": "^2.0.13",
"@tanstack/react-query": "^5.59.16",
"lodash.isequal": "^4.5.0",
"react-markdown": "^9.0.1",
Expand Down
14 changes: 8 additions & 6 deletions packages/connect-react/src/components/ComponentForm.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
import {
FormContextProvider, type FormContext,
} from "../hooks/form-context";
import { DynamicProps } from "../types";
import type {
Component,
ConfigurableProps,
ConfiguredProps,
V1Component,
DynamicProps,
} from "@pipedream/sdk";

import {
type FormContext,
FormContextProvider,
} from "../hooks/form-context";
import { InternalComponentForm } from "./InternalComponentForm";

export type ComponentFormProps<T extends ConfigurableProps, U = ConfiguredProps<T>> = {
Expand All @@ -18,7 +20,7 @@ export type ComponentFormProps<T extends ConfigurableProps, U = ConfiguredProps<
* @deprecated Use `externalUserId` instead.
*/
userId?: string;
component: V1Component<T>;
component: Component;
configuredProps?: U; // XXX value?
disableQueryDisabling?: boolean;
// dynamicPropsId?: string // XXX need to load this initially when passed
Expand Down
2 changes: 1 addition & 1 deletion packages/connect-react/src/components/Control.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ export function Control<T extends ConfigurableProps, U extends ConfigurableProp>
return <RemoteOptionsContainer queryEnabled={queryDisabledIdx == null || queryDisabledIdx >= idx} />;
}

if ("options" in prop && prop.options) {
if ("options" in prop && Array.isArray(prop.options) && prop.options.length > 0) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const options: LabelValueOption<any>[] = prop.options.map(sanitizeOption);
return <ControlSelect options={options} components={{
Expand Down
17 changes: 14 additions & 3 deletions packages/connect-react/src/components/ControlAny.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,14 @@
import { useFormFieldContext } from "../hooks/form-field-context";
import { useCustomize } from "../hooks/customization-context";
import {
ConfiguredPropValueInteger,
ConfiguredPropValueObject,
ConfiguredPropValueString,
ConfiguredPropValueStringArray,
} from "@pipedream/sdk";
import type { CSSProperties } from "react";

import { useCustomize } from "../hooks/customization-context";
import { useFormFieldContext } from "../hooks/form-field-context";

export function ControlAny() {
const formFieldContext = useFormFieldContext();
const {
Expand All @@ -18,7 +25,11 @@ export function ControlAny() {
boxShadow: theme.boxShadow.input,
};

let jsonValue = value;
let jsonValue = value as
| ConfiguredPropValueInteger
| ConfiguredPropValueObject
| ConfiguredPropValueString
| ConfiguredPropValueStringArray;
if (typeof jsonValue === "object") {
jsonValue = JSON.stringify(jsonValue);
}
Expand Down
8 changes: 3 additions & 5 deletions packages/connect-react/src/components/ControlApp.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ export function ControlApp({ app }: ControlAppProps) {
};
const selectProps = select.getProps("controlAppSelect", baseSelectProps);

const oauthAppId = oauthAppConfig?.[app.name_slug];
const oauthAppId = oauthAppConfig?.[app.nameSlug];
const {
isLoading: isLoadingAccounts,
// TODO error
Expand All @@ -86,14 +86,12 @@ export function ControlApp({ app }: ControlAppProps) {
} = useAccounts(
{
external_user_id: externalUserId,
app: app.name_slug,
app: app.nameSlug,
oauth_app_id: oauthAppId,
},
{
useQueryOpts: {
enabled: !!app,

// @ts-expect-error this seems to work (this overrides enabled so don't just set to true)
suspense: !!app,
},
},
Expand Down Expand Up @@ -161,7 +159,7 @@ export function ControlApp({ app }: ControlAppProps) {
isLoading={isLoadingAccounts}
isClearable={true}
isSearchable={true}
getOptionLabel={(a) => a.name}
getOptionLabel={(a) => a.name ?? ""}
getOptionValue={(a) => a.id}
onChange={(a) => {
if (a) {
Expand Down
18 changes: 11 additions & 7 deletions packages/connect-react/src/components/ControlInput.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import type { CSSProperties } from "react";
import { useFormFieldContext } from "../hooks/form-field-context";
import { useCustomize } from "../hooks/customization-context";
import { isConfigurablePropOfType } from "../utils/type-guards";

export function ControlInput() {
const formFieldContextProps = useFormFieldContext();
Expand Down Expand Up @@ -46,20 +47,23 @@ export function ControlInput() {
autoComplete = "new-password"; // in chrome, this is better than "off" here
}

const min = isConfigurablePropOfType(prop, "integer")
? prop.min
: undefined;
const max = isConfigurablePropOfType(prop, "integer")
? prop.max
: undefined;

return (
<input
id={id}
type={inputType}
name={prop.name}
value={value ?? ""}
value={String(value ?? "")}
onChange={(e) => onChange(toOnChangeValue(e.target.value))}
{...getProps("controlInput", baseStyles, formFieldContextProps)}
min={"min" in prop
? prop.min
: undefined}
max={"max" in prop
? prop.max
: undefined}
min={min}
max={max}
autoComplete={autoComplete}
data-lpignore="true"
data-1p-ignore="true"
Expand Down
35 changes: 14 additions & 21 deletions packages/connect-react/src/components/ControlSelect.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import type { PropOptionValue } from "@pipedream/sdk";
import {
useEffect,
useMemo,
Expand All @@ -22,7 +23,7 @@ import {
import { LoadMoreButton } from "./LoadMoreButton";

// XXX T and ConfigurableProp should be related
type ControlSelectProps<T> = {
type ControlSelectProps<T extends PropOptionValue> = {
isCreatable?: boolean;
options: LabelValueOption<T>[];
selectProps?: ReactSelectProps<LabelValueOption<T>, boolean>;
Expand All @@ -31,7 +32,7 @@ type ControlSelectProps<T> = {
components?: ReactSelectProps<LabelValueOption<T>, boolean>["components"];
};

export function ControlSelect<T>({
export function ControlSelect<T extends PropOptionValue>({
isCreatable,
options,
selectProps,
Expand Down Expand Up @@ -83,37 +84,29 @@ export function ControlSelect<T>({
return null;
}

let ret = rawValue;
if (Array.isArray(ret)) {
if (Array.isArray(rawValue)) {
// if simple, make lv (XXX combine this with other place this happens)
if (!isOptionWithLabel(ret[0])) {
return ret.map((o) =>
selectOptions.find((item) => item.value === o) || {
label: String(o),
value: o,
});
if (!isOptionWithLabel(rawValue[0])) {
return rawValue.map((o) =>
selectOptions.find((item) => item.value === o) || sanitizeOption(o as T));
}
} else if (ret && typeof ret === "object" && "__lv" in ret) {
// Extract the actual option from __lv wrapper
ret = ret.__lv;
} else if (!isOptionWithLabel(ret)) {
} else if (rawValue && typeof rawValue === "object" && "__lv" in (rawValue as Record<string, unknown>)) {
// Extract the actual option from __lv wrapper and sanitize to LV
return sanitizeOption(((rawValue as Record<string, unknown>).__lv) as T);
} else if (!isOptionWithLabel(rawValue)) {
const lvOptions = selectOptions?.[0] && isOptionWithLabel(selectOptions[0]);
if (lvOptions) {
for (const item of selectOptions) {
if (item.value === rawValue) {
ret = item;
break;
return item;
}
}
} else {
ret = {
label: String(rawValue),
value: rawValue,
}
return sanitizeOption(rawValue as T);
}
}

return ret;
return null;
}, [
rawValue,
selectOptions,
Expand Down
16 changes: 9 additions & 7 deletions packages/connect-react/src/components/ControlSql.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { useFormFieldContext } from "../hooks/form-field-context";
import { useCustomize } from "../hooks/customization-context";
import type { CSSProperties } from "react";
import type { ConfigurablePropSql } from "@pipedream/sdk";
import { isConfigurablePropOfType } from "../utils/type-guards";

// Type guard to check if value is a structured SQL object
const isSqlStructuredValue = (value: unknown): value is { app: string; query: string; params: unknown[] } => {
Expand All @@ -16,15 +16,17 @@ const isSqlStructuredValue = (value: unknown): value is { app: string; query: st
export function ControlSql() {
const formFieldContext = useFormFieldContext();
const {
id, onChange, prop, value,
id, onChange, prop: sqlProp, value,
} = formFieldContext;

if (!isConfigurablePropOfType(sqlProp, "sql")) {
throw new Error("ControlSql used with non-sql prop");
}

const {
getProps, theme,
} = useCustomize();

// Cast prop to SQL prop type (this component is only used for SQL props)
const sqlProp = prop as ConfigurablePropSql;

// Get the app name from the SQL prop's auth configuration
const appName = sqlProp.auth?.app || "postgresql"; // Default to postgresql

Expand Down Expand Up @@ -65,11 +67,11 @@ export function ControlSql() {
return (
<textarea
id={id}
name={prop.name}
name={sqlProp.name}
value={queryValue}
onChange={(e) => handleChange(e.target.value)}
placeholder="SELECT * FROM table_name"
required={!prop.optional}
required={!sqlProp.optional}
{...getProps("controlSql", baseStyles, formFieldContext)}
/>
);
Expand Down
5 changes: 3 additions & 2 deletions packages/connect-react/src/components/Field.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ export function Field<T extends ConfigurableProp>(props: FieldProps<T>) {
const app = "app" in field.extra
? field.extra.app
: undefined;
if (app && !app.auth_type) {
if (app && !app.authType) {
return null;
}

Expand All @@ -58,8 +58,9 @@ export function Field<T extends ConfigurableProp>(props: FieldProps<T>) {
// XXX rename to FieldErrors + add FormErrors (to ComponentFormInternal)
// XXX use similar pattern as app below for boolean and checkboxing DOM re-ordering?

const fieldProps = props as unknown as FieldProps<ConfigurableProp>;
return (
<div {...getProps("field", baseStyles, props as FieldProps<ConfigurableProp>)}>
<div {...getProps("field", baseStyles, fieldProps)}>
<Label text={labelText} field={field} form={form} />
<Control field={field} form={form} />
<Description markdown={prop.description} field={field} form={form} />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import { ControlSubmit } from "./ControlSubmit";
import type {
ConfigurableProp, ConfigurablePropAlert,
} from "@pipedream/sdk";
import { isConfigurablePropOfType } from "../utils/type-guards";

const alwaysShowSdkErrors = [
"ConfigurationError",
Expand Down Expand Up @@ -56,6 +57,7 @@ export function InternalComponentForm() {
type: "alert",
alertType: "error",
content: `# ${e.name}\n${e.message}`,
name: e.name,
} as ConfigurablePropAlert
}))
}
Expand Down Expand Up @@ -145,7 +147,7 @@ export function InternalComponentForm() {
prop,
idx,
]) => {
if (prop.type === "alert") {
if (isConfigurablePropOfType(prop, "alert")) {
return <Alert key={prop.name} prop={prop} />;
}
return <InternalField key={prop.name} prop={prop} idx={idx} />;
Expand Down
14 changes: 8 additions & 6 deletions packages/connect-react/src/components/InternalField.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
import type { ConfigurableProp } from "@pipedream/sdk";
import type {
ConfigurableProp, ConfigurablePropApp,
} from "@pipedream/sdk";
import { FormFieldContext } from "../hooks/form-field-context";
import { useFormContext } from "../hooks/form-context";
import { Field } from "./Field";
import { useApp } from "../hooks/use-app";
import { useEffect } from "react";
import { isConfigurablePropOfType } from "../utils/type-guards";

type FieldInternalProps<T extends ConfigurableProp> = {
prop: T;
Expand All @@ -18,17 +21,16 @@ export function InternalField<T extends ConfigurableProp>({
id: formId, configuredProps, registerField, setConfiguredProp, errors, enableDebugging,
} = formCtx;

const appSlug = prop.type === "app" && "app" in prop
? prop.app
: undefined;
let appSlug: ConfigurablePropApp["app"] | undefined;
if (isConfigurablePropOfType(prop, "app")) {
appSlug = prop.app;
}
const {
// TODO error
app,
} = useApp(appSlug || "", {
useQueryOpts: {
enabled: !!appSlug,

// @ts-expect-error this seems to work (this overrides enabled so don't just set to true)
suspense: !!appSlug,
},
});
Expand Down
Loading
Loading