Skip to content

Commit

Permalink
Making formspree-react (and core) compatible with new version of reca…
Browse files Browse the repository at this point in the history
…ptcha v3 (#19)

* adding better promise detection for extradata in react

* adding ReCaptcha demo. Handling legacy error format

* fixes to payment form / styles

* better error handling

* updated versions
colevscode authored Nov 1, 2022
1 parent 452ddc4 commit cc355c4
Showing 19 changed files with 312 additions and 144 deletions.
1 change: 0 additions & 1 deletion examples/cra-demo/package.json
Original file line number Diff line number Diff line change
@@ -14,7 +14,6 @@
"dev": "react-scripts start",
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject",
"clean": "rm -rf build && rm -rf node_modules"
},
32 changes: 21 additions & 11 deletions examples/cra-demo/src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,43 +1,53 @@
import { useState } from 'react';
import { FormspreeProvider } from '@formspree/react';
import PaymentForm from './PaymentForm';
import SimpleForm from './SimpleForm';
import { useState } from "react";
import { FormspreeProvider } from "@formspree/react";
import PaymentForm from "./PaymentForm";
import SimpleForm from "./SimpleForm";
import RecaptchaForm from "./RecaptchaForm";

const App = () => {
const [isStripe, setStripe] = useState(false);
const [tab, setTab] = useState("simple");

return (
<>
<div className="container">
<div className="tabs">
<button
type="button"
className={`tab ${!isStripe && 'active'}`}
onClick={() => setStripe(false)}
className={`tab ${tab === "simple" && "active"}`}
onClick={() => setTab("simple")}
>
Simple form
</button>
<button
type="button"
className={`tab ${isStripe && 'active'}`}
onClick={() => setStripe(true)}
className={`tab ${tab === "recaptcha" && "active"}`}
onClick={() => setTab("recaptcha")}
>
ReCaptcha form
</button>
<button
type="button"
className={`tab ${tab === "stripe" && "active"}`}
onClick={() => setTab("stripe")}
>
Stripe form
</button>
</div>
{isStripe ? (
{tab === "stripe" ? (
<FormspreeProvider
stripePK={process.env.REACT_APP_STRIPE_PUBLISHABLE_KEY}
>
<PaymentForm />
</FormspreeProvider>
) : tab === "recaptcha" ? (
<RecaptchaForm />
) : (
<SimpleForm />
)}
</div>
<footer className="container">
<p>
Powered by:{' '}
Powered by:{" "}
<a href="https://formspree.io?utm_source=formspree-react-demo">
Formspree
</a>
132 changes: 64 additions & 68 deletions examples/cra-demo/src/PaymentForm.tsx
Original file line number Diff line number Diff line change
@@ -1,21 +1,21 @@
import { useMemo } from 'react';
import { useForm, CardElement, ValidationError } from '@formspree/react';
import CardExample from './CardExample';
import { useMemo } from "react";
import { useForm, CardElement, ValidationError } from "@formspree/react";
import CardExample from "./CardExample";

const useOptions = () => {
const options = useMemo(
() => ({
style: {
base: {
color: '#424770',
letterSpacing: '0.025em',
fontFamily: 'Source Code Pro, monospace',
'::placeholder': {
color: '#aab7c4',
color: "#424770",
letterSpacing: "0.025em",
fontFamily: "Source Code Pro, monospace",
"::placeholder": {
color: "#aab7c4",
},
},
invalid: {
color: '#9e2146',
color: "#9e2146",
},
},
}),
@@ -31,66 +31,62 @@ const PaymentForm = () => {
process.env.REACT_APP_PAYMENT_FORM_ID as string
);

return (
<div
style={{
maxWidth: 960,
margin: '0 auto',
fontFamily: 'Helvetica Neue, Helvetica, Arial, sans-serif',
padding: '4rem 0',
}}
>
{state && state.succeeded ? (
<h2>Payment has been handled successfully!</h2>
) : (
<form onSubmit={handleSubmit}>
<div className="block">
<label htmlFor="email">Email</label>
<input id="email" type="email" name="email" />
</div>
<div className="block">
<label htmlFor="email">Card details</label>
<CardElement options={options} />
<ValidationError
className="error"
field="paymentMethod"
errors={state.errors}
/>
</div>
<button type="submit" disabled={state.submitting}>
{state.submitting ? 'Handling payment...' : 'Pay'}
</button>
return state && state.succeeded ? (
<h2>Payment has been handled successfully!</h2>
) : (
<form onSubmit={handleSubmit}>
<div className="block">
<label htmlFor="email">Email</label>
<input id="email" type="email" name="email" />
<ValidationError
field="email"
prefix="Email"
className="error"
errors={state.errors}
/>
</div>
<div className="block">
<label htmlFor="email">Card details</label>
<CardElement options={options} />
<ValidationError
className="error"
field="paymentMethod"
errors={state.errors}
/>
</div>
<div className="block">
<ValidationError className="error" errors={state.errors} />
</div>
<button type="submit" disabled={state.submitting}>
{state.submitting ? "Handling payment..." : "Pay"}
</button>

<div className="block info">
<p>You can use the following cards for testing:</p>
<ul>
<CardExample
title="Successful charge: 4242 4242 4242 4242"
cardNumber="4242424242424242"
/>
<CardExample
title="Declined payment: 4000 0000 0000 0002"
cardNumber="4000000000000002"
/>
<CardExample
title="3D secure: 4000 0027 6000 3184"
cardNumber="4000002760003184"
/>
</ul>
<span>
Use any 3 digits for CVC and any future date for the date
</span>
<a
href="https://stripe.com/docs/testing"
target="_blank"
rel="noreferrer"
>
See more on Stripe
</a>
</div>
</form>
)}
</div>
<div className="block info">
<p>You can use the following cards for testing:</p>
<ul>
<CardExample
title="Successful charge: 4242 4242 4242 4242"
cardNumber="4242424242424242"
/>
<CardExample
title="Declined payment: 4000 0000 0000 0002"
cardNumber="4000000000000002"
/>
<CardExample
title="3D secure: 4000 0027 6000 3184"
cardNumber="4000002760003184"
/>
</ul>
<span>Use any 3 digits for CVC and any future date for the date</span>
<a
href="https://stripe.com/docs/testing"
target="_blank"
rel="noreferrer"
>
See more on Stripe
</a>
</div>
</form>
);
};

73 changes: 73 additions & 0 deletions examples/cra-demo/src/RecaptchaForm.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import { useForm, ValidationError } from "@formspree/react";
import { useState } from "react";

import {
GoogleReCaptchaProvider,
useGoogleReCaptcha,
} from "react-google-recaptcha-v3";

const ReCaptchaForm = () => {
const { executeRecaptcha } = useGoogleReCaptcha();
const [failReCaptcha, setFailReCaptcha] = useState(false);
const [state, handleSubmit] = useForm(
process.env.REACT_APP_RECAPTCHA_FORM_ID as string,
{
data: {
"g-recaptcha-response": failReCaptcha
? () => new Promise<string>((resolve) => resolve("Nonsense!"))
: executeRecaptcha,
},
}
);

return (
<div>
{state && state.succeeded ? (
<h2>Your message has been sent successfully!</h2>
) : (
<form onSubmit={handleSubmit}>
<div className="block">
<label htmlFor="email">Email</label>
<input id="email" type="email" name="email" />
<ValidationError
field="email"
prefix="Email"
className="error"
errors={state.errors}
/>
</div>
<div className="block">
<label htmlFor="name">Name</label>
<input id="name" type="name" name="name" />
</div>
<div className="block">
<label className="forCheckbox" htmlFor="failRecaptcha">
Fail Recaptcha
</label>
<input
id="failReCaptcha"
type="checkbox"
onChange={(ev) => {
setFailReCaptcha(ev.target.checked);
}}
/>
</div>
<div className="block">
<ValidationError className="error" errors={state.errors} />
</div>
<button type="submit" disabled={state.submitting}>
{state.submitting ? "Submitting..." : "Submit"}
</button>
</form>
)}
</div>
);
};

export default () => (
<GoogleReCaptchaProvider
reCaptchaKey={process.env.REACT_APP_RECAPTCHA_KEY as string}
>
<ReCaptchaForm />
</GoogleReCaptchaProvider>
);
26 changes: 14 additions & 12 deletions examples/cra-demo/src/SimpleForm.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
import {
useForm,
ValidationError,
} from '@formspree/react';
import { useForm, ValidationError } from "@formspree/react";

const SimpleForm = () => {
const [state, handleSubmit] = useForm(process.env.REACT_APP_SIMPLE_FORM_ID as string);
const [state, handleSubmit] = useForm(
process.env.REACT_APP_SIMPLE_FORM_ID as string
);

return (
<div>
@@ -15,6 +14,12 @@ const SimpleForm = () => {
<div className="block">
<label htmlFor="email">Email</label>
<input id="email" type="email" name="email" />
<ValidationError
field="email"
prefix="Email"
className="error"
errors={state.errors}
/>
</div>
<div className="block">
<label htmlFor="name">Name</label>
@@ -25,18 +30,15 @@ const SimpleForm = () => {
<textarea id="message" name="message" rows={10} />
</div>
<div className="block">
<ValidationError
className="error"
errors={state.errors}
/>
<ValidationError className="error" errors={state.errors} />
</div>
<button type="submit" disabled={state.submitting}>
{state.submitting ? 'Submitting...' : 'Submit'}
{state.submitting ? "Submitting..." : "Submit"}
</button>
</form>
)}
</div>
);
}
};

export default SimpleForm
export default SimpleForm;
Loading
Oops, something went wrong.

1 comment on commit cc355c4

@vercel
Copy link

@vercel vercel bot commented on cc355c4 Nov 1, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.