@pipedream/connect-react 1.0.0-preview.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,41 @@
1
+ Pipedream Source Available License Version 1.0
2
+
3
+ This Pipedream Source Available License Agreement Version 1.0 (this “Agreement”) sets forth the terms on which Pipedream, Inc., a Delaware corporation (“Pipedream”) makes available certain software (the “Software”). By installing, downloading, accessing, using or distributing the Software, you agree to the terms of this Agreement. If you do not agree to such terms, you must not use the Software. If you are receiving the Software on behalf of a legal entity, you represent and warrant that you have the actual authority to agree to the terms and conditions of this Agreement on behalf of such entity. “Licensee” means you, an individual, or the entity on whose behalf you are receiving the Software.
4
+
5
+ LICENSE GRANT AND CONDITIONS.
6
+
7
+ 1.1 License. Subject to the terms and conditions of this Agreement, Pipedream hereby grants to Licensee a non-exclusive, royalty-free, worldwide, non-transferable, non-sublicenseable license during the term of this Agreement to use, prepare modifications and derivative works, distribute (including without limitation in source code or object code form) and reproduce copies of the Software (the “License”). Licensee is not granted the right to, and Licensee shall not, exercise the License for an Excluded Purpose. For purposes of this Agreement, “Excluded Purpose” means any commercial use of the software including, but not limited to, making available any software-as-a-service, platform-as-a-service, infrastructure-as-a-service or other online service that competes with the Software or any other Pipedream products or services.
8
+
9
+ 1.2 Conditions. In consideration of the License, Licensee’s distribution of the Software is subject to the following conditions:
10
+
11
+ a. Licensee must cause any Software modified by Licensee to carry prominent notices stating that Licensee modified the Software.
12
+
13
+ b. On each Software copy, Licensee shall reproduce and not remove or alter all Pipedream or third party copyright or other proprietary notices contained in the Software, and Licensee must provide this with each copy: “This software is made available by Pipedream, Inc., under the terms of the Pipedream Source Available License, Version 1.0 located at https://github.com/PipedreamHQ/pipedream/blob/master/LICENSE. By installing, downloading, accessing, using or distributing the Software, you agree to the terms of such License Agreement.”
14
+
15
+ 1.3 Modifications. Licensee may add its own copyright notices to modifications made by Licensee and may provide additional or different license terms and conditions for use, reproduction, or distribution of Licensee’s modifications. While redistributing the Software or modifications thereof, Licensee may choose to offer, for a fee or free of charge, support, warranty, indemnity, or other obligations. Licensee, and not Pipedream, will be responsible for any such obligations.
16
+
17
+ 1.4 No Sublicensing. The License does not include the right to sublicense the Software; provided, however, that any recipient to which Licensee provides the Software may exercise the License so long as such recipient agrees to the terms and conditions of this Agreement.
18
+
19
+ TERM AND TERMINATION.
20
+
21
+ This Agreement will continue unless and until earlier terminated as set forth herein. If Licensee breaches any of its conditions or obligations under this Agreement, this Agreement will terminate automatically and the License will terminate automatically and permanently.
22
+
23
+ INTELLECTUAL PROPERTY.
24
+
25
+ As between the parties, Pipedream retains all right, title, and interest in the Software, and all intellectual property rights therein. Pipedream hereby reserves all rights not expressly granted to Licensee in this Agreement. Pipedream reserves all rights in its trademarks and service marks, and no licenses thereto are granted by this Agreement.
26
+
27
+ DISCLAIMER.
28
+
29
+ PIPEDREAM HEREBY DISCLAIMS ANY AND ALL WARRANTIES AND CONDITIONS, EXPRESS, IMPLIED, STATUTORY, OR OTHERWISE, AND SPECIFICALLY DISCLAIMS ANY WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, WITH RESPECT TO THE SOFTWARE.
30
+
31
+ LIMITATION OF LIABILITY.
32
+
33
+ PIPEDREAM WILL NOT BE LIABLE FOR ANY DAMAGES OF ANY KIND, INCLUDING BUT NOT LIMITED TO, LOST PROFITS OR ANY CONSEQUENTIAL, SPECIAL, INCIDENTAL, INDIRECT, OR DIRECT DAMAGES, HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, ARISING OUT OF THIS AGREEMENT. THE FOREGOING SHALL APPLY TO THE MAXIMUM EXTENT PERMITTED BY APPLICABLE LAW.
34
+
35
+ GENERAL.
36
+
37
+ 6.1 Governing Law. This Agreement will be governed by and interpreted in accordance with the laws of the state of California, without reference to its conflict of laws principles. If Licensee is located outside of the United States, any dispute, controversy or claim arising out of or relating to this Agreement will be referred to and finally determined by arbitration in accordance with the JAMS International Arbitration Rules. The tribunal will consist of one arbitrator. Judgment upon the award rendered by the arbitrator may be entered in any court having jurisdiction thereof. All disputes including arbitrations shall take place in Santa Clara County, California. The language to be used in any such proceedings shall be English.
38
+
39
+ 6.2. Assignment. Licensee is not authorized to assign its rights under this Agreement to any third party. Pipedream may freely assign its rights under this Agreement to any third party.
40
+
41
+ 6.3. Other. This Agreement is the entire agreement between the parties regarding the subject matter hereof. No amendment or modification of this Agreement will be valid or binding upon the parties unless made in writing and signed by the duly authorized representatives of both parties. In the event that any provision, including without limitation any condition, of this Agreement is held to be unenforceable, this Agreement and all licenses and rights granted hereunder will immediately terminate. Waiver by Pipedream of a breach of any provision of this Agreement or the failure by Pipedream to exercise any right hereunder will not be construed as a waiver of any subsequent breach of that right or as a waiver of any other right.
package/README.md ADDED
@@ -0,0 +1,430 @@
1
+ # Pipedream Connect-React
2
+
3
+ > [!IMPORTANT]
4
+ > This package is in early preview and is undergoing rapid development, polish, and expanding customization capabilities.
5
+ >
6
+ > Please join [our community](https://pipedream.com/support) or reach out to `connect@pipedream.com` with feedback.
7
+
8
+ ## Installation and usage
9
+
10
+ ```sh
11
+ % npm install --save @pipedream/connect-react
12
+ ```
13
+
14
+ Then use the `ComponentFormContainer` component in your app (see below for props
15
+ and customization).
16
+
17
+ > [!NOTE]
18
+ > To run the example below, set the following environment variables in `.env.local`.
19
+ >
20
+ > `PIPEDREAM_ALLOWED_ORIGINS`
21
+ >
22
+ > Make sure this matches the origin of your app, e.g.
23
+ >
24
+ > ```sh
25
+ > # One domain — note the array
26
+ > PIPEDREAM_ALLOWED_ORIGINS=["https://example.com"]
27
+ >
28
+ > # Multiple domains
29
+ > PIPEDREAM_ALLOWED_ORIGINS=["https://example.com", "http://localhost:3000"]
30
+ > ```
31
+ >
32
+ > `PIPEDREAM_CLIENT_ID`
33
+ >
34
+ > Create a [Pipedream API OAuth client](https://pipedream.com/docs/rest-api/auth#creating-an-oauth-client) and enter its client ID here.
35
+ >
36
+ > `PIPEDREAM_CLIENT_SECRET`
37
+ >
38
+ > Your OAuth client secret. **This is a secret, and should not be exposed to your frontend**.
39
+ >
40
+ > `PIPEDREAM_PROJECT_ID`
41
+ >
42
+ > [Create a Pipedream project](https://pipedream.com/projects) and [copy its ID](https://pipedream.com/docs/projects#finding-your-projects-id).
43
+
44
+ ```typescript
45
+ /* actions.ts */
46
+ "use server";
47
+ import { createBackendClient } from "@pipedream/sdk/server";
48
+
49
+ const {
50
+ NODE_ENV,
51
+ PIPEDREAM_ALLOWED_ORIGINS,
52
+ PIPEDREAM_CLIENT_ID,
53
+ PIPEDREAM_CLIENT_SECRET,
54
+ PIPEDREAM_PROJECT_ID,
55
+ } = process.env;
56
+
57
+ const allowedOrigins = JSON.parse(PIPEDREAM_ALLOWED_ORIGINS || "[]");
58
+
59
+ const client = createBackendClient({
60
+ environment: NODE_ENV,
61
+ projectId: PIPEDREAM_PROJECT_ID,
62
+ credentials: {
63
+ clientId: PIPEDREAM_CLIENT_ID,
64
+ clientSecret: PIPEDREAM_CLIENT_SECRET,
65
+ },
66
+ });
67
+
68
+ export async function fetchToken(opts: { externalUserId: string }) {
69
+ return await client.createConnectToken({
70
+ external_user_id: opts.externalUserId,
71
+ allowed_origins: PIPEDREAM_ALLOWED_ORIGINS,
72
+ });
73
+ }
74
+
75
+ /* page.tsx */
76
+ "use client";
77
+ import { useState } from "react";
78
+ import { createFrontendClient } from "@pipedream/sdk/browser";
79
+ import {
80
+ ComponentFormContainer,
81
+ FrontendClientProvider,
82
+ } from "@pipedream/connect-react";
83
+ import { fetchToken } from "./actions";
84
+
85
+ export default function Page() {
86
+ // https://pipedream.com/docs/connect/api#external-users
87
+ const userId = "my-authed-user-id";
88
+ const client = createFrontendClient({
89
+ environment: "development",
90
+ externalUserId: userId,
91
+ tokenCallback: fetchToken,
92
+ });
93
+ const [configuredProps, setConfiguredProps] = useState({
94
+ text: "hello slack!",
95
+ });
96
+ return (
97
+ <>
98
+ <div>My application</div>
99
+ <FrontendClientProvider client={client}>
100
+ <ComponentFormContainer
101
+ userId={userId}
102
+ componentKey="slack-send-message"
103
+ configuredProps={configuredProps}
104
+ onUpdateConfiguredProps={setConfiguredProps}
105
+ />
106
+ </FrontendClientProvider>
107
+ </>
108
+ );
109
+ }
110
+ ```
111
+
112
+ ## Components and Props
113
+
114
+ ### `FrontendClientProvider`
115
+
116
+ Necessary wrapper to provide the frontend client to other components.
117
+
118
+ ```typescript
119
+ type Props = {
120
+ client: typeof import("@pipedream/sdk/browser").createFrontendClient;
121
+ };
122
+ ```
123
+
124
+ ### `ComponentFormContainer`
125
+
126
+ Loader component for `ComponentForm`.
127
+
128
+ ```typescript
129
+ type ComponentFormContainerProps = {
130
+ /** action (or trigger) to look up in the [Pipedream Registry](https://github.com/PipedreamHQ/pipedream/tree/master/components) */
131
+ componentKey: string;
132
+ } & Omit<ComponentFormProps, "component">; // see below
133
+ ```
134
+
135
+ ### `ComponentForm`
136
+
137
+ ```typescript
138
+ type ComponentFormProps = {
139
+ component: typeof import("@pipedream/sdk").V1Component;
140
+ /** External user configuring the form */
141
+ userId: string;
142
+ /** Form configured values */
143
+ configuredProps?: Record<string, any>;
144
+ /** Filtering configurable props */
145
+ propNames?: string[];
146
+ /** Shows submit button + callback when clicked */
147
+ onSubmit: (ctx: FormContext) => Awaitable<void>;
148
+ /** To control and store configured values on form updates, can be used to call actionRun or triggerDeploy */
149
+ onUpdateConfiguredProps: (v: Record<string, any>) => void;
150
+ /** Hide optional props section */
151
+ hideOptionalProps: boolean;
152
+ };
153
+ ```
154
+
155
+ ## Customization
156
+
157
+ Style individual components using the `CustomizeProvider` and a `CustomizationConfig`.
158
+
159
+ ```tsx
160
+ <FrontendClientProvider client={client}>
161
+ <CustomizeProvider {...customizationConfig}>
162
+ <ComponentFormContainer
163
+ key="slack-send-message"
164
+ configuredProps={configuredProps}
165
+ onUpdateConfiguredProps={setConfiguredProps}
166
+ />
167
+ </CustomizeProvider>
168
+ </FrontendClientProvider>
169
+ ```
170
+
171
+ ```typescript
172
+ type CustomizationConfig = {
173
+ classNames?: CustomClassNamesConfig;
174
+ classNamePrefix?: string;
175
+ components?: CustomComponentsConfig;
176
+ styles?: CustomStylesConfig;
177
+ theme?: CustomThemeConfig;
178
+ unstyled?: boolean;
179
+ };
180
+ ```
181
+
182
+ ### The `classNames` prop
183
+
184
+ Not to be confused with the `className` prop, `classNames` takes an object with
185
+ keys to represent the inner components of a `ComponentForm`. Each inner
186
+ component takes a callback function with the following signature:
187
+
188
+ ```tsx
189
+ <CustomizeProvider
190
+ classNames={{
191
+ controlInput: ({ prop }) =>
192
+ prop.type === "number" ? "border-red-600" : "border-blue-600",
193
+ }}
194
+ />
195
+ ```
196
+
197
+ #### Note on CSS specificity
198
+
199
+ If you're using the `classNames` API and you're trying to override some base
200
+ styles with the same level of specificity, you must ensure that your provided
201
+ styles are declared later than the styles from `@pipedream/connect-react`
202
+ (e.g. the link or style tag in the head of your HTML document) in order for them
203
+ to take precedence.
204
+
205
+ ### The `classNamePrefix` prop
206
+
207
+ If you provide the `classNamePrefix` prop, all inner elements will be given a
208
+ `className` with the provided prefix.
209
+
210
+ ### The `components` prop
211
+
212
+ You can rewrite individual inner components with the `components` prop. If you
213
+ want to respect other customizations, such as `styles`, `classNames` or `theme`,
214
+ use the `useCustomize` hook to integrate your base styles with the customization
215
+ framework.
216
+
217
+ In this example, we're replacing the default `Label` inner component with a version
218
+ that adds a checkmark to highlight required props.
219
+
220
+ ```tsx
221
+ import type { CSSProperties } from "react";
222
+ import type { ConfigurableProp, LabelProps } from "@pipedream/connect-react";
223
+ import { useCustomize } from "@pipedream/connect-react";
224
+
225
+ export function CustomLabel<T extends ConfigurableProp>(props: LabelProps<T>) {
226
+ const { text, field } = props;
227
+ const { id } = field;
228
+
229
+ const { getProps, theme } = useCustomize();
230
+
231
+ const baseStyles: CSSProperties = {
232
+ color: theme.colors.neutral90,
233
+ fontWeight: 450,
234
+ gridArea: "label",
235
+ textTransform: "capitalize",
236
+ lineHeight: "1.5",
237
+ };
238
+
239
+ const required =
240
+ !field.prop.optional && !["alert", "app"].includes(field.prop.type) ? (
241
+ field.prop.type == "boolean" ? (
242
+ typeof field.value != "undefined"
243
+ ) : !!field.value ? (
244
+ <span
245
+ style={{ color: "#12b825", fontSize: "small", marginLeft: "0.5rem" }}
246
+ >
247
+ {" "}
248
+
249
+ </span>
250
+ ) : (
251
+ <span
252
+ style={{ color: "#d0d0d0", fontSize: "small", marginLeft: "0.5rem" }}
253
+ >
254
+ {" "}
255
+
256
+ </span>
257
+ )
258
+ ) : (
259
+ ""
260
+ );
261
+ return (
262
+ <label htmlFor={id} {...getProps("label", baseStyles, props)}>
263
+ {text}
264
+ {required}
265
+ </label>
266
+ );
267
+ }
268
+ ```
269
+
270
+ Then we apply the custom component using `CustomizeProvider`.
271
+
272
+ ```tsx
273
+ import { CustomLabel } from "./CustomLabel";
274
+
275
+ <CustomizeProvider
276
+ components={{
277
+ Label: CustomLabel,
278
+ }}
279
+ />;
280
+ ```
281
+
282
+ ### The `styles` prop
283
+
284
+ The recommended way to provide custom styles is to use the `styles` prop. Each inner
285
+ component takes either a CSSProperties object or a callback function with the following
286
+ signature:
287
+
288
+ ```tsx
289
+ <CustomizeProvider
290
+ styles={{
291
+ label: { fontSize: "80%" },
292
+ controlInput: (base, { theme }) => ({
293
+ ...base,
294
+ borderTop: 0,
295
+ borderLeft: 0,
296
+ borderRight: 0,
297
+ border: "solid",
298
+ borderColor: theme.colors.primary,
299
+ backgroundColor: theme.colors.neutral0,
300
+ }),
301
+ }}
302
+ />
303
+ ```
304
+
305
+ Note that when using the callback function, the `styles` prop has access to `theme`
306
+ customizations as well.
307
+
308
+ ### The `theme` prop
309
+
310
+ The default styles are derived from a theme object, which can be customized using
311
+ the `theme` prop.
312
+
313
+ ```tsx
314
+ <CustomizeProvider
315
+ theme={{
316
+ borderRadius: 0,
317
+ colors: {
318
+ primary: "hsl(200, 100%, 60%)",
319
+ primary75: "hsl(200, 100%, 55%)",
320
+ primary50: "hsl(200, 100%, 40%)",
321
+ primary25: "hsl(200, 100%, 35%)",
322
+
323
+ danger: "#DE350B",
324
+ dangerLight: "#FFBDAD",
325
+
326
+ neutral0: "hsl(200, 50%, 97%)",
327
+ neutral5: "hsl(200, 50%, 95%)",
328
+ neutral10: "hsl(200, 50%, 90%)",
329
+ neutral20: "hsl(200, 50%, 80%)",
330
+ neutral30: "hsl(200, 50%, 70%)",
331
+ neutral40: "hsl(200, 50%, 60%)",
332
+ neutral50: "hsl(200, 50%, 50%)",
333
+ neutral60: "hsl(200, 50%, 40%)",
334
+ neutral70: "hsl(200, 50%, 30%)",
335
+ neutral80: "hsl(200, 50%, 20%)",
336
+ neutral90: "hsl(200, 50%, 10%)",
337
+ },
338
+ spacing: {
339
+ baseUnit: 4,
340
+ controlHeight: 10,
341
+ menuGutter: 6,
342
+ },
343
+ }}
344
+ />
345
+ ```
346
+
347
+ ### The `unstyled` prop
348
+
349
+ While it is always possible to override default styling, you may prefer to start
350
+ from completely unstyled components. Add the `unstyled` prop to remove all styling.
351
+
352
+ ```tsx
353
+ <CustomizeProvider unstyled={true} />
354
+ ```
355
+
356
+ ## Inner components
357
+
358
+ The following list shows all of the customizable inner components used in a
359
+ `ComponentForm`.
360
+
361
+ ```typescript
362
+ export type CustomizableProps = {
363
+ componentForm: ComponentProps<typeof ComponentForm>;
364
+ connectButton: ComponentProps<typeof ControlApp> &
365
+ FormFieldContext<ConfigurableProp>;
366
+ controlAny: ComponentProps<typeof ControlAny> &
367
+ FormFieldContext<ConfigurableProp>;
368
+ controlApp: ComponentProps<typeof ControlApp> &
369
+ FormFieldContext<ConfigurableProp>;
370
+ controlBoolean: ComponentProps<typeof ControlBoolean> &
371
+ FormFieldContext<ConfigurableProp>;
372
+ controlInput: ComponentProps<typeof ControlInput> &
373
+ FormFieldContext<ConfigurableProp>;
374
+ controlSubmit: ComponentProps<typeof ControlSubmit>;
375
+ description: ComponentProps<typeof Description>;
376
+ error: ComponentProps<typeof Errors>;
377
+ errors: ComponentProps<typeof Errors>;
378
+ field: ComponentProps<typeof Field>;
379
+ heading: ComponentProps<typeof ComponentForm>;
380
+ label: ComponentProps<typeof Label>;
381
+ optionalFields: ComponentProps<typeof ComponentForm>;
382
+ optionalFieldButton: ComponentProps<typeof OptionalFieldButton>;
383
+ };
384
+ ```
385
+
386
+ Internally, `@pipedream/connect-react` uses `react-select` to implement complex
387
+ dropdown components.
388
+
389
+ ```typescript
390
+ export type ReactSelectComponents = {
391
+ controlAppSelect: typeof ControlApp;
392
+ controlSelect: typeof ControlSelect;
393
+ };
394
+ ```
395
+
396
+ Customizing dropdown components is very similar to customizing other inner components
397
+ except each dropdown supports deeper customization of elements within `react-select`
398
+
399
+ ```tsx
400
+ <CustomizeProvider
401
+ styles={{
402
+ controlSelect: {
403
+ control: (base, { theme }) => ({
404
+ ...base,
405
+ borderRadius: 0,
406
+ borderColor: theme.colors.primary25,
407
+ fontSize: "small",
408
+ maxHeight: "36px",
409
+ }),
410
+ },
411
+ }}
412
+ />
413
+ ```
414
+
415
+ See [React Select](https://react-select.com/) for more details on the
416
+ customization options available.
417
+
418
+ ## Hooks
419
+
420
+ - `useCustomize` — _see above_
421
+ - `useFormContext`
422
+ - `useFormFieldContext`
423
+ - `useFrontendClient` — _allows use of provided Pipedream frontendClient_
424
+ - `useAccounts` — _react-query wrapper to list Pipedream connect accounts (for app, external user, etc.)_
425
+ - `useApp` — _react-query wrapper to retrieve a Pipedream app_
426
+ - `useApps` — _react-query wrapper to list Pipedream apps_
427
+ - `useComponent` — _react-query wrapper to retrieve a Pipedream component (action or trigger)_
428
+ - `useComponents` — _react-query wrapper to list Pipedream components (actions or triggers)_
429
+
430
+ See [hooks](./src/hooks) folder for details.