npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@pipedream/connect-react

v1.0.0-preview.15

Published

Pipedream Connect library for React

Downloads

1,579

Readme

Pipedream Connect-React

[!IMPORTANT] This package is in early preview and is undergoing rapid development, polish, and expanding customization capabilities.

Please join our community or reach out to [email protected] with feedback.

Installation and usage

% npm install --save @pipedream/connect-react

Then use the ComponentFormContainer component in your app (see below for props and customization).

[!NOTE] To run the example below, set the following environment variables in .env.local.

PIPEDREAM_ALLOWED_ORIGINS

Make sure this matches the origin of your app, e.g.

# One domain — note the array
PIPEDREAM_ALLOWED_ORIGINS=["https://example.com"]

# Multiple domains
PIPEDREAM_ALLOWED_ORIGINS=["https://example.com", "http://localhost:3000"]

PIPEDREAM_CLIENT_ID

Create a Pipedream API OAuth client and enter its client ID here.

PIPEDREAM_CLIENT_SECRET

Your OAuth client secret. This is a secret, and should not be exposed to your frontend.

PIPEDREAM_PROJECT_ID

Create a Pipedream project and copy its ID.

/* actions.ts */
"use server";
import { createBackendClient } from "@pipedream/sdk/server";

const {
  NODE_ENV,
  PIPEDREAM_ALLOWED_ORIGINS,
  PIPEDREAM_CLIENT_ID,
  PIPEDREAM_CLIENT_SECRET,
  PIPEDREAM_PROJECT_ID,
} = process.env;

const allowedOrigins = JSON.parse(PIPEDREAM_ALLOWED_ORIGINS || "[]");

const client = createBackendClient({
  environment: NODE_ENV,
  projectId: PIPEDREAM_PROJECT_ID,
  credentials: {
    clientId: PIPEDREAM_CLIENT_ID,
    clientSecret: PIPEDREAM_CLIENT_SECRET,
  },
});

export async function fetchToken(opts: { externalUserId: string }) {
  return await client.createConnectToken({
    external_user_id: opts.externalUserId,
    allowed_origins: PIPEDREAM_ALLOWED_ORIGINS,
  });
}

/* page.tsx */
"use client";
import { useState } from "react";
import { createFrontendClient } from "@pipedream/sdk/browser";
import {
  ComponentFormContainer,
  FrontendClientProvider,
} from "@pipedream/connect-react";
import { fetchToken } from "./actions";

export default function Page() {
  // https://pipedream.com/docs/connect/api#external-users
  const userId = "my-authed-user-id";
  const client = createFrontendClient({
    environment: "development",
    externalUserId: userId,
    tokenCallback: fetchToken,
  });
  const [configuredProps, setConfiguredProps] = useState({
    text: "hello slack!",
  });
  return (
    <>
      <div>My application</div>
      <FrontendClientProvider client={client}>
        <ComponentFormContainer
          userId={userId}
          componentKey="slack-send-message"
          configuredProps={configuredProps}
          onUpdateConfiguredProps={setConfiguredProps}
        />
      </FrontendClientProvider>
    </>
  );
}

Components and Props

FrontendClientProvider

Necessary wrapper to provide the frontend client to other components.

type Props = {
  client: typeof import("@pipedream/sdk/browser").createFrontendClient;
};

ComponentFormContainer

Loader component for ComponentForm.

type ComponentFormContainerProps = {
  /** action (or trigger) to look up in the [Pipedream Registry](https://github.com/PipedreamHQ/pipedream/tree/master/components) */
  componentKey: string;
} & Omit<ComponentFormProps, "component">; // see below

ComponentForm

type ComponentFormProps = {
  component: typeof import("@pipedream/sdk").V1Component;
  /** External user configuring the form */
  userId: string;
  /** Form configured values */
  configuredProps?: Record<string, any>;
  /** Filtering configurable props */
  propNames?: string[];
  /** Shows submit button + callback when clicked */
  onSubmit: (ctx: FormContext) => Awaitable<void>;
  /** To control and store configured values on form updates, can be used to call actionRun or triggerDeploy */
  onUpdateConfiguredProps: (v: Record<string, any>) => void;
  /** Hide optional props section */
  hideOptionalProps: boolean;
};

Customization

Style individual components using the CustomizeProvider and a CustomizationConfig.

<FrontendClientProvider client={client}>
  <CustomizeProvider {...customizationConfig}>
    <ComponentFormContainer
      key="slack-send-message"
      configuredProps={configuredProps}
      onUpdateConfiguredProps={setConfiguredProps}
    />
  </CustomizeProvider>
</FrontendClientProvider>
type CustomizationConfig = {
  classNames?: CustomClassNamesConfig;
  classNamePrefix?: string;
  components?: CustomComponentsConfig;
  styles?: CustomStylesConfig;
  theme?: CustomThemeConfig;
  unstyled?: boolean;
};

The classNames prop

Not to be confused with the className prop, classNames takes an object with keys to represent the inner components of a ComponentForm. Each inner component takes a callback function with the following signature:

<CustomizeProvider
  classNames={{
    controlInput: ({ prop }) =>
      prop.type === "number" ? "border-red-600" : "border-blue-600",
  }}
/>

Note on CSS specificity

If you're using the classNames API and you're trying to override some base styles with the same level of specificity, you must ensure that your provided styles are declared later than the styles from @pipedream/connect-react (e.g. the link or style tag in the head of your HTML document) in order for them to take precedence.

The classNamePrefix prop

If you provide the classNamePrefix prop, all inner elements will be given a className with the provided prefix.

The components prop

You can rewrite individual inner components with the components prop. If you want to respect other customizations, such as styles, classNames or theme, use the useCustomize hook to integrate your base styles with the customization framework.

In this example, we're replacing the default Label inner component with a version that adds a checkmark to highlight required props.

import type { CSSProperties } from "react";
import type { ConfigurableProp, LabelProps } from "@pipedream/connect-react";
import { useCustomize } from "@pipedream/connect-react";

export function CustomLabel<T extends ConfigurableProp>(props: LabelProps<T>) {
  const { text, field } = props;
  const { id } = field;

  const { getProps, theme } = useCustomize();

  const baseStyles: CSSProperties = {
    color: theme.colors.neutral90,
    fontWeight: 450,
    gridArea: "label",
    textTransform: "capitalize",
    lineHeight: "1.5",
  };

  const required =
    !field.prop.optional && !["alert", "app"].includes(field.prop.type) ? (
      field.prop.type == "boolean" ? (
        typeof field.value != "undefined"
      ) : !!field.value ? (
        <span
          style={{ color: "#12b825", fontSize: "small", marginLeft: "0.5rem" }}
        >
          {" "}
          ✓
        </span>
      ) : (
        <span
          style={{ color: "#d0d0d0", fontSize: "small", marginLeft: "0.5rem" }}
        >
          {" "}
          ✓
        </span>
      )
    ) : (
      ""
    );
  return (
    <label htmlFor={id} {...getProps("label", baseStyles, props)}>
      {text}
      {required}
    </label>
  );
}

Then we apply the custom component using CustomizeProvider.

import { CustomLabel } from "./CustomLabel";

<CustomizeProvider
  components={{
    Label: CustomLabel,
  }}
/>;

The styles prop

The recommended way to provide custom styles is to use the styles prop. Each inner component takes either a CSSProperties object or a callback function with the following signature:

<CustomizeProvider
  styles={{
    label: { fontSize: "80%" },
    controlInput: (base, { theme }) => ({
      ...base,
      borderTop: 0,
      borderLeft: 0,
      borderRight: 0,
      border: "solid",
      borderColor: theme.colors.primary,
      backgroundColor: theme.colors.neutral0,
    }),
  }}
/>

Note that when using the callback function, the styles prop has access to theme customizations as well.

The theme prop

The default styles are derived from a theme object, which can be customized using the theme prop.

<CustomizeProvider
  theme={{
    borderRadius: 0,
    colors: {
      primary: "hsl(200, 100%, 60%)",
      primary75: "hsl(200, 100%, 55%)",
      primary50: "hsl(200, 100%, 40%)",
      primary25: "hsl(200, 100%, 35%)",

      danger: "#DE350B",
      dangerLight: "#FFBDAD",

      neutral0: "hsl(200, 50%, 97%)",
      neutral5: "hsl(200, 50%, 95%)",
      neutral10: "hsl(200, 50%, 90%)",
      neutral20: "hsl(200, 50%, 80%)",
      neutral30: "hsl(200, 50%, 70%)",
      neutral40: "hsl(200, 50%, 60%)",
      neutral50: "hsl(200, 50%, 50%)",
      neutral60: "hsl(200, 50%, 40%)",
      neutral70: "hsl(200, 50%, 30%)",
      neutral80: "hsl(200, 50%, 20%)",
      neutral90: "hsl(200, 50%, 10%)",
    },
    spacing: {
      baseUnit: 4,
      controlHeight: 10,
      menuGutter: 6,
    },
  }}
/>

The unstyled prop

While it is always possible to override default styling, you may prefer to start from completely unstyled components. Add the unstyled prop to remove all styling.

<CustomizeProvider unstyled={true} />

Inner components

The following list shows all of the customizable inner components used in a ComponentForm.

export type CustomizableProps = {
  componentForm: ComponentProps<typeof ComponentForm>;
  connectButton: ComponentProps<typeof ControlApp> &
    FormFieldContext<ConfigurableProp>;
  controlAny: ComponentProps<typeof ControlAny> &
    FormFieldContext<ConfigurableProp>;
  controlApp: ComponentProps<typeof ControlApp> &
    FormFieldContext<ConfigurableProp>;
  controlBoolean: ComponentProps<typeof ControlBoolean> &
    FormFieldContext<ConfigurableProp>;
  controlInput: ComponentProps<typeof ControlInput> &
    FormFieldContext<ConfigurableProp>;
  controlSubmit: ComponentProps<typeof ControlSubmit>;
  description: ComponentProps<typeof Description>;
  error: ComponentProps<typeof Errors>;
  errors: ComponentProps<typeof Errors>;
  field: ComponentProps<typeof Field>;
  heading: ComponentProps<typeof ComponentForm>;
  label: ComponentProps<typeof Label>;
  optionalFields: ComponentProps<typeof ComponentForm>;
  optionalFieldButton: ComponentProps<typeof OptionalFieldButton>;
};

Internally, @pipedream/connect-react uses react-select to implement complex dropdown components.

export type ReactSelectComponents = {
  controlAppSelect: typeof ControlApp;
  controlSelect: typeof ControlSelect;
};

Customizing dropdown components is very similar to customizing other inner components except each dropdown supports deeper customization of elements within react-select

<CustomizeProvider
  styles={{
    controlSelect: {
      control: (base, { theme }) => ({
        ...base,
        borderRadius: 0,
        borderColor: theme.colors.primary25,
        fontSize: "small",
        maxHeight: "36px",
      }),
    },
  }}
/>

See React Select for more details on the customization options available.

Hooks

  • useCustomizesee above
  • useFormContext
  • useFormFieldContext
  • useFrontendClientallows use of provided Pipedream frontendClient
  • useAccountsreact-query wrapper to list Pipedream connect accounts (for app, external user, etc.)
  • useAppreact-query wrapper to retrieve a Pipedream app
  • useAppsreact-query wrapper to list Pipedream apps
  • useComponentreact-query wrapper to retrieve a Pipedream component (action or trigger)
  • useComponentsreact-query wrapper to list Pipedream components (actions or triggers)

See hooks folder for details.