From 1c73a090da9cf574e7f0cfd5cb324a937b74c378 Mon Sep 17 00:00:00 2001 From: tjliu-stripe Date: Mon, 10 Aug 2026 14:08:32 -0400 Subject: [PATCH 1/4] Upgrade storybook to v10 (#694) * bump node version * update storybook * bump ci build action node version * bump eslint-plugin-react --- .github/workflows/build.yml | 4 +- .node-version | 1 + .storybook/constants.ts | 3 + .storybook/example.stories.js | 42 - .storybook/main.js | 22 - .storybook/main.ts | 49 + examples/.eslintrc.yml | 2 +- .../0-Card-Minimal.stories.tsx | 16 + .../{0-Card-Minimal.js => 0-Card-Minimal.tsx} | 17 +- .../1-Card-Detailed.stories.tsx | 16 + ...1-Card-Detailed.js => 1-Card-Detailed.tsx} | 82 +- .../class-components/2-Split-Card.stories.tsx | 16 + .../{2-Split-Card.js => 2-Split-Card.tsx} | 28 +- .../3-Payment-Request-Button.stories.tsx | 16 + ...Button.js => 3-Payment-Request-Button.tsx} | 34 +- examples/class-components/4-IBAN.stories.tsx | 16 + .../{4-IBAN.js => 4-IBAN.tsx} | 28 +- examples/hooks/0-Card-Minimal.stories.tsx | 16 + .../{0-Card-Minimal.js => 0-Card-Minimal.tsx} | 5 +- examples/hooks/1-Card-Detailed.stories.tsx | 16 + ...1-Card-Detailed.js => 1-Card-Detailed.tsx} | 66 +- examples/hooks/11-Custom-Checkout.stories.tsx | 16 + ...tom-Checkout.js => 11-Custom-Checkout.tsx} | 59 +- .../hooks/12-Embedded-Checkout.stories.tsx | 16 + ...d-Checkout.js => 12-Embedded-Checkout.tsx} | 18 +- examples/hooks/13-Checkout-Form.stories.tsx | 16 + ...-Checkout-Form.js => 13-Checkout-Form.tsx} | 41 +- examples/hooks/14-Issuing-Elements.tsx | 19 +- examples/hooks/2-Split-Card.stories.tsx | 16 + .../{2-Split-Card.js => 2-Split-Card.tsx} | 15 +- .../3-Payment-Request-Button.stories.tsx | 16 + ...Button.js => 3-Payment-Request-Button.tsx} | 18 +- examples/hooks/4-IBAN.stories.tsx | 16 + examples/hooks/{4-IBAN.js => 4-IBAN.tsx} | 15 +- examples/hooks/9-Payment-Element.stories.tsx | 16 + ...yment-Element.js => 9-Payment-Element.tsx} | 30 +- examples/{util.js => util.tsx} | 12 +- package.json | 10 +- yarn.lock | 7868 ++++++----------- 39 files changed, 3202 insertions(+), 5510 deletions(-) create mode 100644 .node-version create mode 100644 .storybook/constants.ts delete mode 100644 .storybook/example.stories.js delete mode 100644 .storybook/main.js create mode 100644 .storybook/main.ts create mode 100644 examples/class-components/0-Card-Minimal.stories.tsx rename examples/class-components/{0-Card-Minimal.js => 0-Card-Minimal.tsx} (82%) create mode 100644 examples/class-components/1-Card-Detailed.stories.tsx rename examples/class-components/{1-Card-Detailed.js => 1-Card-Detailed.tsx} (82%) create mode 100644 examples/class-components/2-Split-Card.stories.tsx rename examples/class-components/{2-Split-Card.js => 2-Split-Card.tsx} (87%) create mode 100644 examples/class-components/3-Payment-Request-Button.stories.tsx rename examples/class-components/{3-Payment-Request-Button.js => 3-Payment-Request-Button.tsx} (83%) create mode 100644 examples/class-components/4-IBAN.stories.tsx rename examples/class-components/{4-IBAN.js => 4-IBAN.tsx} (85%) create mode 100644 examples/hooks/0-Card-Minimal.stories.tsx rename examples/hooks/{0-Card-Minimal.js => 0-Card-Minimal.tsx} (94%) create mode 100644 examples/hooks/1-Card-Detailed.stories.tsx rename examples/hooks/{1-Card-Detailed.js => 1-Card-Detailed.tsx} (84%) create mode 100644 examples/hooks/11-Custom-Checkout.stories.tsx rename examples/hooks/{11-Custom-Checkout.js => 11-Custom-Checkout.tsx} (70%) create mode 100644 examples/hooks/12-Embedded-Checkout.stories.tsx rename examples/hooks/{12-Embedded-Checkout.js => 12-Embedded-Checkout.tsx} (79%) create mode 100644 examples/hooks/13-Checkout-Form.stories.tsx rename examples/hooks/{13-Checkout-Form.js => 13-Checkout-Form.tsx} (67%) create mode 100644 examples/hooks/2-Split-Card.stories.tsx rename examples/hooks/{2-Split-Card.js => 2-Split-Card.tsx} (89%) create mode 100644 examples/hooks/3-Payment-Request-Button.stories.tsx rename examples/hooks/{3-Payment-Request-Button.js => 3-Payment-Request-Button.tsx} (86%) create mode 100644 examples/hooks/4-IBAN.stories.tsx rename examples/hooks/{4-IBAN.js => 4-IBAN.tsx} (87%) create mode 100644 examples/hooks/9-Payment-Element.stories.tsx rename examples/hooks/{9-Payment-Element.js => 9-Payment-Element.tsx} (76%) rename examples/{util.js => util.tsx} (62%) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 39b8f67f..19e295e5 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -9,9 +9,9 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - - uses: actions/setup-node@v1 + - uses: actions/setup-node@v4 with: - node-version: 20.x + node-version: 24.x - run: yarn install --frozen-lockfile - run: yarn run lint:prettier - run: yarn run lint diff --git a/.node-version b/.node-version new file mode 100644 index 00000000..cf2efde8 --- /dev/null +++ b/.node-version @@ -0,0 +1 @@ +24.13.0 \ No newline at end of file diff --git a/.storybook/constants.ts b/.storybook/constants.ts new file mode 100644 index 00000000..446d0a8d --- /dev/null +++ b/.storybook/constants.ts @@ -0,0 +1,3 @@ +export const defaultParameter = { + layout: 'fullscreen' as const, +}; diff --git a/.storybook/example.stories.js b/.storybook/example.stories.js deleted file mode 100644 index f2d7dd54..00000000 --- a/.storybook/example.stories.js +++ /dev/null @@ -1,42 +0,0 @@ -// @noflow -/* eslint-disable import/no-extraneous-dependencies */ -import {storiesOf, module} from '@storybook/react'; -import React, {useEffect, useState} from 'react'; - -const ExampleComponent = ({file}) => { - const [example, setExample] = useState(null); - - useEffect(() => { - import(`../examples/${file}`).then(({default: Example}) => { - setExample(); - }); - }, []); - - return example; -}; - -const addDemo = (directory, file, stories) => { - const name = file - .replace(/\.(js|tsx?)$/, '') - .split('-') - .slice(1) - .join(' '); - - stories.add(name, () => ); -}; - -const hooksStories = storiesOf('react-stripe-js/Hooks', module); -require - .context('../examples/hooks/', false, /\/\d+-(.*)\.(js|tsx?)$/) - .keys() - .forEach((key) => { - addDemo('hooks', key.slice(2), hooksStories); - }); - -const classStories = storiesOf('react-stripe-js/Class Components', module); -require - .context('../examples/class-components/', false, /\/\d+-(.*).js$/) - .keys() - .forEach((key) => { - addDemo('class-components', key.slice(2), classStories); - }); diff --git a/.storybook/main.js b/.storybook/main.js deleted file mode 100644 index b4dd6417..00000000 --- a/.storybook/main.js +++ /dev/null @@ -1,22 +0,0 @@ -module.exports = { - stories: ['./example.stories.js'], - reactOptions: { - strictMode: true, - }, - webpackFinal: async (config) => { - config.module.rules.push({ - test: /\.tsx?$/, - exclude: /node_modules/, - use: [ - { - loader: require.resolve('ts-loader'), - options: { - transpileOnly: true, - }, - }, - ], - }); - config.resolve.extensions.push('.ts', '.tsx'); - return config; - }, -}; diff --git a/.storybook/main.ts b/.storybook/main.ts new file mode 100644 index 00000000..a6de159e --- /dev/null +++ b/.storybook/main.ts @@ -0,0 +1,49 @@ +import type {StorybookConfig} from '@storybook/react-webpack5'; +import type {Configuration} from 'webpack'; +import {fileURLToPath} from 'url'; +import {dirname, resolve} from 'path'; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = dirname(__filename); + +const config: StorybookConfig = { + stories: ['../examples/**/*.stories.@(tsx|ts|jsx|js)'], + framework: { + name: '@storybook/react-webpack5', + options: {fastRefresh: true, builder: {lazyCompilation: true}}, + }, + addons: [], + webpackFinal: async (config: Configuration) => { + // Add babel-loader rule for JSX/TS files in .storybook, examples, and src directories + // Use unshift to add this rule before Storybook's internal rules + config.module?.rules?.unshift({ + test: /\.(jsx?|tsx?)$/, + include: [ + resolve(__dirname, '../src'), + resolve(__dirname, '../.storybook'), + resolve(__dirname, '../examples'), + ], + use: { + loader: 'babel-loader', + options: { + presets: [ + ['@babel/preset-react', {runtime: 'automatic'}], + '@babel/preset-env', + '@babel/preset-typescript', + ], + plugins: [ + [ + '@babel/plugin-transform-runtime', + { + regenerator: true, + }, + ], + ], + }, + }, + }); + return config; + }, +}; + +export default config; diff --git a/examples/.eslintrc.yml b/examples/.eslintrc.yml index e140e505..b75dd048 100644 --- a/examples/.eslintrc.yml +++ b/examples/.eslintrc.yml @@ -1,4 +1,4 @@ --- -extends: '../.eslintrc.yml' +extends: ["../.eslintrc.yml", "plugin:react/jsx-runtime"] rules: import/no-extraneous-dependencies: 0 diff --git a/examples/class-components/0-Card-Minimal.stories.tsx b/examples/class-components/0-Card-Minimal.stories.tsx new file mode 100644 index 00000000..63ed2a04 --- /dev/null +++ b/examples/class-components/0-Card-Minimal.stories.tsx @@ -0,0 +1,16 @@ +import type {Meta, StoryObj} from '@storybook/react'; + +import {defaultParameter} from '../../.storybook/constants'; +import CardMinimal from './0-Card-Minimal'; + +const meta: Meta = { + title: 'react-stripe-js/Class Components/Card Minimal', + component: CardMinimal, + parameters: defaultParameter, +}; + +export default meta; + +type Story = StoryObj; + +export const Default: Story = {}; diff --git a/examples/class-components/0-Card-Minimal.js b/examples/class-components/0-Card-Minimal.tsx similarity index 82% rename from examples/class-components/0-Card-Minimal.js rename to examples/class-components/0-Card-Minimal.tsx index 6c90d2f9..95df07e8 100644 --- a/examples/class-components/0-Card-Minimal.js +++ b/examples/class-components/0-Card-Minimal.tsx @@ -1,14 +1,19 @@ // This example shows you how to set up React Stripe.js and use Elements. // Learn how to accept a payment using the official Stripe docs. // https://stripe.com/docs/payments/accept-a-payment#web - -import React from 'react'; -import {loadStripe} from '@stripe/stripe-js'; +import type {FormEventHandler} from 'react'; +import {Component} from 'react'; +import {loadStripe, Stripe, StripeElements} from '@stripe/stripe-js'; import {CardElement, Elements, ElementsConsumer} from '../../src'; import '../styles/common.css'; -class CheckoutForm extends React.Component { - handleSubmit = async (event) => { +interface CheckoutFormProps { + stripe: Stripe | null; + elements: StripeElements | null; +} + +class CheckoutForm extends Component { + handleSubmit: FormEventHandler = async (event) => { // Block native form submission. event.preventDefault(); @@ -79,7 +84,7 @@ const InjectedCheckoutForm = () => { ); }; -// Make sure to call `loadStripe` outside of a component’s render to avoid +// Make sure to call `loadStripe` outside of a component's render to avoid // recreating the `Stripe` object on every render. const stripePromise = loadStripe('pk_test_6pRNASCoBOKtIshFeQd4XMUh'); diff --git a/examples/class-components/1-Card-Detailed.stories.tsx b/examples/class-components/1-Card-Detailed.stories.tsx new file mode 100644 index 00000000..0ffb6916 --- /dev/null +++ b/examples/class-components/1-Card-Detailed.stories.tsx @@ -0,0 +1,16 @@ +import type {Meta, StoryObj} from '@storybook/react'; + +import {defaultParameter} from '../../.storybook/constants'; +import CardDetailed from './1-Card-Detailed'; + +const meta: Meta = { + title: 'react-stripe-js/Class Components/Card Detailed', + component: CardDetailed, + parameters: defaultParameter, +}; + +export default meta; + +type Story = StoryObj; + +export const Default: Story = {}; diff --git a/examples/class-components/1-Card-Detailed.js b/examples/class-components/1-Card-Detailed.tsx similarity index 82% rename from examples/class-components/1-Card-Detailed.js rename to examples/class-components/1-Card-Detailed.tsx index eb5b640b..8057b9ef 100644 --- a/examples/class-components/1-Card-Detailed.js +++ b/examples/class-components/1-Card-Detailed.tsx @@ -2,15 +2,23 @@ // Learn how to accept a payment using the official Stripe docs. // https://stripe.com/docs/payments/accept-a-payment#web -import React from 'react'; -import {loadStripe} from '@stripe/stripe-js'; +import type {ChangeEvent, FormEventHandler, ReactNode} from 'react'; +import {Component} from 'react'; +import { + loadStripe, + Stripe, + StripeElements, + PaymentMethod, + StripeError, + StripeCardElementChangeEvent, +} from '@stripe/stripe-js'; import {CardElement, Elements, ElementsConsumer} from '../../src'; import '../styles/common.css'; import '../styles/2-Card-Detailed.css'; const CARD_OPTIONS = { - iconStyle: 'solid', + iconStyle: 'solid' as const, style: { base: { iconColor: '#c4f0ff', @@ -33,12 +41,27 @@ const CARD_OPTIONS = { }, }; -const CardField = ({onChange}) => ( +interface CardFieldProps { + onChange: (event: StripeCardElementChangeEvent) => void; +} + +const CardField = ({onChange}: CardFieldProps) => (
); +interface FieldProps { + label: string; + id: string; + type: string; + placeholder: string; + required: boolean; + autoComplete: string; + value: string; + onChange: (event: ChangeEvent) => void; +} + const Field = ({ label, id, @@ -48,7 +71,7 @@ const Field = ({ autoComplete, value, onChange, -}) => ( +}: FieldProps) => (
); -const SubmitButton = ({processing, error, children, disabled}) => ( +interface SubmitButtonProps { + processing: boolean; + error: StripeError | null | undefined; + children: ReactNode; + disabled: boolean; +} + +const SubmitButton = ({ + processing, + error, + children, + disabled, +}: SubmitButtonProps) => ( ); -const ErrorMessage = ({children}) => ( +interface ErrorMessageProps { + children: ReactNode; +} + +const ErrorMessage = ({children}: ErrorMessageProps) => (
(
); -const ResetButton = ({onClick}) => ( +interface ResetButtonProps { + onClick: () => void; +} + +const ResetButton = ({onClick}: ResetButtonProps) => ( ); -const DEFAULT_STATE = { +interface CheckoutFormProps { + stripe: Stripe | null; + elements: StripeElements | null; +} + +interface CheckoutFormState { + error: StripeError | null | undefined; + cardComplete: boolean; + processing: boolean; + paymentMethod: PaymentMethod | null; + email: string; + phone: string; + name: string; +} + +const DEFAULT_STATE: CheckoutFormState = { error: null, cardComplete: false, processing: false, @@ -113,13 +171,13 @@ const DEFAULT_STATE = { name: '', }; -class CheckoutForm extends React.Component { - constructor(props) { +class CheckoutForm extends Component { + constructor(props: CheckoutFormProps) { super(props); this.state = DEFAULT_STATE; } - handleSubmit = async (event) => { + handleSubmit: FormEventHandler = async (event) => { event.preventDefault(); const {stripe, elements} = this.props; diff --git a/examples/class-components/2-Split-Card.stories.tsx b/examples/class-components/2-Split-Card.stories.tsx new file mode 100644 index 00000000..d2a43757 --- /dev/null +++ b/examples/class-components/2-Split-Card.stories.tsx @@ -0,0 +1,16 @@ +import type {Meta, StoryObj} from '@storybook/react'; + +import {defaultParameter} from '../../.storybook/constants'; +import SplitCard from './2-Split-Card'; + +const meta: Meta = { + title: 'react-stripe-js/Class Components/Split Card', + component: SplitCard, + parameters: defaultParameter, +}; + +export default meta; + +type Story = StoryObj; + +export const Default: Story = {}; diff --git a/examples/class-components/2-Split-Card.js b/examples/class-components/2-Split-Card.tsx similarity index 87% rename from examples/class-components/2-Split-Card.js rename to examples/class-components/2-Split-Card.tsx index 29a84b77..4b455898 100644 --- a/examples/class-components/2-Split-Card.js +++ b/examples/class-components/2-Split-Card.tsx @@ -2,8 +2,14 @@ // Learn how to accept a payment using the official Stripe docs. // https://stripe.com/docs/payments/accept-a-payment#web -import React from 'react'; -import {loadStripe} from '@stripe/stripe-js'; +import type {FormEventHandler} from 'react'; +import {Component} from 'react'; +import { + loadStripe, + Stripe, + StripeElements, + PaymentMethod, +} from '@stripe/stripe-js'; import { CardNumberElement, CardCvcElement, @@ -31,8 +37,20 @@ const ELEMENT_OPTIONS = { }, }; -class CheckoutForm extends React.Component { - constructor(props) { +interface CheckoutFormProps { + stripe: Stripe | null; + elements: StripeElements | null; +} + +interface CheckoutFormState { + name: string; + postal: string; + errorMessage: string | null | undefined; + paymentMethod: PaymentMethod | null; +} + +class CheckoutForm extends Component { + constructor(props: CheckoutFormProps) { super(props); this.state = { name: '', @@ -42,7 +60,7 @@ class CheckoutForm extends React.Component { }; } - handleSubmit = async (event) => { + handleSubmit: FormEventHandler = async (event) => { event.preventDefault(); const {stripe, elements} = this.props; const {name, postal} = this.state; diff --git a/examples/class-components/3-Payment-Request-Button.stories.tsx b/examples/class-components/3-Payment-Request-Button.stories.tsx new file mode 100644 index 00000000..03c779e8 --- /dev/null +++ b/examples/class-components/3-Payment-Request-Button.stories.tsx @@ -0,0 +1,16 @@ +import type {Meta, StoryObj} from '@storybook/react'; + +import {defaultParameter} from '../../.storybook/constants'; +import PaymentRequestButton from './3-Payment-Request-Button'; + +const meta: Meta = { + title: 'react-stripe-js/Class Components/Payment Request Button', + component: PaymentRequestButton, + parameters: defaultParameter, +}; + +export default meta; + +type Story = StoryObj; + +export const Default: Story = {}; diff --git a/examples/class-components/3-Payment-Request-Button.js b/examples/class-components/3-Payment-Request-Button.tsx similarity index 83% rename from examples/class-components/3-Payment-Request-Button.js rename to examples/class-components/3-Payment-Request-Button.tsx index 8e4a7843..7f892732 100644 --- a/examples/class-components/3-Payment-Request-Button.js +++ b/examples/class-components/3-Payment-Request-Button.tsx @@ -2,8 +2,13 @@ // Learn how to accept a payment with the PaymentRequestButton using the official Stripe docs. // https://stripe.com/docs/stripe-js/elements/payment-request-button#react -import React from 'react'; -import {loadStripe} from '@stripe/stripe-js'; +import {Component} from 'react'; +import { + loadStripe, + Stripe, + PaymentMethod, + PaymentRequest, +} from '@stripe/stripe-js'; import { PaymentRequestButtonElement, Elements, @@ -33,14 +38,27 @@ const NotAvailableResult = () => ( const ELEMENT_OPTIONS = { style: { paymentRequestButton: { - type: 'buy', - theme: 'dark', + type: 'buy' as const, + theme: 'dark' as const, }, }, }; -class CheckoutForm extends React.Component { - constructor(props) { +interface CheckoutFormProps { + stripe: Stripe | null; +} + +interface CheckoutFormState { + canMakePayment: boolean; + hasCheckedAvailability: boolean; + errorMessage: string | null; + paymentMethod?: PaymentMethod; +} + +class CheckoutForm extends Component { + paymentRequest?: PaymentRequest; + + constructor(props: CheckoutFormProps) { super(props); this.state = { canMakePayment: false, @@ -58,7 +76,7 @@ class CheckoutForm extends React.Component { } } - async createPaymentRequest(stripe) { + async createPaymentRequest(stripe: Stripe) { this.paymentRequest = stripe.paymentRequest({ country: 'US', currency: 'usd', @@ -90,7 +108,7 @@ class CheckoutForm extends React.Component { } = this.state; return (
- {canMakePayment && ( + {canMakePayment && this.paymentRequest && ( { if (paymentMethod) { diff --git a/examples/class-components/4-IBAN.stories.tsx b/examples/class-components/4-IBAN.stories.tsx new file mode 100644 index 00000000..bd6875ef --- /dev/null +++ b/examples/class-components/4-IBAN.stories.tsx @@ -0,0 +1,16 @@ +import type {Meta, StoryObj} from '@storybook/react'; + +import {defaultParameter} from '../../.storybook/constants'; +import IBAN from './4-IBAN'; + +const meta: Meta = { + title: 'react-stripe-js/Class Components/IBAN', + component: IBAN, + parameters: defaultParameter, +}; + +export default meta; + +type Story = StoryObj; + +export const Default: Story = {}; diff --git a/examples/class-components/4-IBAN.js b/examples/class-components/4-IBAN.tsx similarity index 85% rename from examples/class-components/4-IBAN.js rename to examples/class-components/4-IBAN.tsx index c185f124..0af0b8bc 100644 --- a/examples/class-components/4-IBAN.js +++ b/examples/class-components/4-IBAN.tsx @@ -2,8 +2,14 @@ // Learn how to accept a SEPA Debit payment using the official Stripe docs. // https://stripe.com/docs/payments/sepa-debit/accept-a-payment -import React from 'react'; -import {loadStripe} from '@stripe/stripe-js'; +import type {FormEventHandler} from 'react'; +import {Component} from 'react'; +import { + loadStripe, + Stripe, + PaymentMethod, + StripeElements, +} from '@stripe/stripe-js'; import {IbanElement, Elements, ElementsConsumer} from '../../src'; import {logEvent, Result, ErrorResult} from '../util'; @@ -26,13 +32,25 @@ const ELEMENT_OPTIONS = { }, }; -class CheckoutForm extends React.Component { - constructor(props) { +interface CheckoutFormProps { + stripe: Stripe | null; + elements: StripeElements | null; +} + +interface CheckoutFormState { + name: string; + email: string; + errorMessage: string | null | undefined; + paymentMethod: PaymentMethod | null; +} + +class CheckoutForm extends Component { + constructor(props: CheckoutFormProps) { super(props); this.state = {name: '', email: '', errorMessage: null, paymentMethod: null}; } - handleSubmit = async (event) => { + handleSubmit: FormEventHandler = async (event) => { event.preventDefault(); const {stripe, elements} = this.props; diff --git a/examples/hooks/0-Card-Minimal.stories.tsx b/examples/hooks/0-Card-Minimal.stories.tsx new file mode 100644 index 00000000..c1fc8d86 --- /dev/null +++ b/examples/hooks/0-Card-Minimal.stories.tsx @@ -0,0 +1,16 @@ +import type {Meta, StoryObj} from '@storybook/react'; + +import {defaultParameter} from '../../.storybook/constants'; +import CardMinimal from './0-Card-Minimal'; + +const meta: Meta = { + title: 'react-stripe-js/Hooks/Card Minimal', + component: CardMinimal, + parameters: defaultParameter, +}; + +export default meta; + +type Story = StoryObj; + +export const Default: Story = {}; diff --git a/examples/hooks/0-Card-Minimal.js b/examples/hooks/0-Card-Minimal.tsx similarity index 94% rename from examples/hooks/0-Card-Minimal.js rename to examples/hooks/0-Card-Minimal.tsx index 4c2da416..ccb3a983 100644 --- a/examples/hooks/0-Card-Minimal.js +++ b/examples/hooks/0-Card-Minimal.tsx @@ -1,8 +1,7 @@ // This example shows you how to set up React Stripe.js and use Elements. // Learn how to accept a payment using the official Stripe docs. // https://stripe.com/docs/payments/accept-a-payment#web - -import React from 'react'; +import type {FormEventHandler} from 'react'; import {loadStripe} from '@stripe/stripe-js'; import {CardElement, Elements, useElements, useStripe} from '../../src'; @@ -12,7 +11,7 @@ const CheckoutForm = () => { const stripe = useStripe(); const elements = useElements(); - const handleSubmit = async (event) => { + const handleSubmit: FormEventHandler = async (event) => { // Block native form submission. event.preventDefault(); diff --git a/examples/hooks/1-Card-Detailed.stories.tsx b/examples/hooks/1-Card-Detailed.stories.tsx new file mode 100644 index 00000000..9dec56bd --- /dev/null +++ b/examples/hooks/1-Card-Detailed.stories.tsx @@ -0,0 +1,16 @@ +import type {Meta, StoryObj} from '@storybook/react'; + +import {defaultParameter} from '../../.storybook/constants'; +import CardDetailed from './1-Card-Detailed'; + +const meta: Meta = { + title: 'react-stripe-js/Hooks/Card Detailed', + component: CardDetailed, + parameters: defaultParameter, +}; + +export default meta; + +type Story = StoryObj; + +export const Default: Story = {}; diff --git a/examples/hooks/1-Card-Detailed.js b/examples/hooks/1-Card-Detailed.tsx similarity index 84% rename from examples/hooks/1-Card-Detailed.js rename to examples/hooks/1-Card-Detailed.tsx index dccd3743..268a0e5b 100644 --- a/examples/hooks/1-Card-Detailed.js +++ b/examples/hooks/1-Card-Detailed.tsx @@ -2,15 +2,26 @@ // Learn how to accept a payment using the official Stripe docs. // https://stripe.com/docs/payments/accept-a-payment#web -import React, {useState} from 'react'; -import {loadStripe} from '@stripe/stripe-js'; +import type { + ChangeEvent, + ChangeEventHandler, + ReactNode, + FormEventHandler, +} from 'react'; +import {useState} from 'react'; +import { + loadStripe, + PaymentMethod, + StripeCardElementChangeEvent, + StripeError, +} from '@stripe/stripe-js'; import {CardElement, Elements, useElements, useStripe} from '../../src'; import '../styles/common.css'; import '../styles/2-Card-Detailed.css'; const CARD_OPTIONS = { - iconStyle: 'solid', + iconStyle: 'solid' as const, style: { base: { iconColor: '#c4f0ff', @@ -33,7 +44,11 @@ const CARD_OPTIONS = { }, }; -const CardField = ({onChange}) => ( +const CardField = ({ + onChange, +}: { + onChange: (event: StripeCardElementChangeEvent) => void; +}) => (
@@ -48,6 +63,15 @@ const Field = ({ autoComplete, value, onChange, +}: { + label: string; + id: string; + type: string; + placeholder: string; + required?: boolean; + autoComplete?: string; + value: string; + onChange: ChangeEventHandler; }) => (
); -const SubmitButton = ({processing, error, children, disabled}) => ( +const SubmitButton = ({ + processing, + error, + children, + disabled, +}: { + processing: boolean; + error: StripeError | StripeCardElementChangeEvent['error'] | null; + children: ReactNode; + disabled?: boolean; +}) => ( ); -const ErrorMessage = ({children}) => ( +const ErrorMessage = ({children}: {children: ReactNode}) => (
(
); -const ResetButton = ({onClick}) => ( +const ResetButton = ({onClick}: {onClick: () => void}) => (