@blocklet/payment-react 1.20.4 → 1.20.6
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/.aigne/doc-smith/config.yaml +2 -2
- package/.aigne/doc-smith/upload-cache.yaml +381 -0
- package/docs/components-business-auto-topup.md +128 -179
- package/docs/components-business-overdue-invoice-payment.md +108 -143
- package/docs/components-business-resume-subscription.md +117 -104
- package/docs/components-business.md +12 -36
- package/docs/components-checkout-checkout-donate.md +209 -149
- package/docs/components-checkout-checkout-form.md +115 -136
- package/docs/components-checkout-checkout-table.md +92 -172
- package/docs/components-checkout.md +43 -109
- package/docs/components-history-credit-grants-list.md +45 -70
- package/docs/components-history-credit-transactions-list.md +57 -67
- package/docs/components-history-invoice-list.md +58 -52
- package/docs/components-history-payment-list.md +19 -40
- package/docs/components-history.md +42 -67
- package/docs/components-ui-form-elements-address-form.md +37 -65
- package/docs/components-ui-form-elements-country-select.md +80 -59
- package/docs/components-ui-form-elements-currency-selector.md +57 -73
- package/docs/components-ui-form-elements-phone-input.md +90 -112
- package/docs/components-ui-form-elements.md +46 -80
- package/docs/components-ui-payment-summary.md +71 -119
- package/docs/components-ui-pricing-table.md +117 -204
- package/docs/components-ui.md +59 -32
- package/docs/components.md +89 -62
- package/docs/getting-started.md +36 -63
- package/docs/guides-theming.md +122 -84
- package/docs/guides-utilities.md +107 -145
- package/docs/guides.md +7 -84
- package/docs/hooks-use-mobile.md +50 -36
- package/docs/hooks-use-subscription.md +72 -89
- package/docs/hooks.md +12 -82
- package/docs/overview.md +45 -52
- package/docs/providers-donate-provider.md +73 -95
- package/docs/providers-payment-provider.md +115 -169
- package/docs/providers.md +27 -86
- package/es/locales/en.js +7 -0
- package/es/locales/zh.js +8 -1
- package/es/payment/index.js +3 -0
- package/es/payment/progress-item.d.ts +12 -0
- package/es/payment/progress-item.js +78 -0
- package/es/payment/success.d.ts +4 -1
- package/es/payment/success.js +55 -3
- package/lib/locales/en.js +7 -0
- package/lib/locales/zh.js +8 -1
- package/lib/payment/index.js +3 -0
- package/lib/payment/progress-item.d.ts +12 -0
- package/lib/payment/progress-item.js +107 -0
- package/lib/payment/success.d.ts +4 -1
- package/lib/payment/success.js +59 -3
- package/package.json +7 -7
- package/src/locales/en.tsx +7 -0
- package/src/locales/zh.tsx +8 -1
- package/src/payment/index.tsx +6 -0
- package/src/payment/progress-item.tsx +107 -0
- package/src/payment/success.tsx +88 -3
|
@@ -1,105 +1,126 @@
|
|
|
1
1
|
# CountrySelect
|
|
2
2
|
|
|
3
|
-
The `CountrySelect` component
|
|
3
|
+
The `CountrySelect` component provides a user-friendly dropdown for selecting a country. It features a searchable list, displays country flags, and offers a responsive design that adapts to both desktop and mobile viewports. It is designed to be used within a form managed by `react-hook-form`.
|
|
4
4
|
|
|
5
5
|
## Props
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
|
10
|
-
|
|
|
11
|
-
| `
|
|
12
|
-
| `
|
|
13
|
-
| `
|
|
14
|
-
| `sx` | `SxProps` | No | `{}` | Custom styles applied to the root element using the MUI `sx` prop. |
|
|
15
|
-
| `showDialCode` | `boolean` | No | `false` | If `true`, the country's dial code is displayed next to its name in the list. |
|
|
7
|
+
| Prop | Type | Description | Required | Default |
|
|
8
|
+
|--------------|----------------------------------|---------------------------------------------------------------------------------------------|----------|---------|
|
|
9
|
+
| `value` | `CountryIso2` | The ISO2 code of the selected country (e.g., 'us'). | Yes | |
|
|
10
|
+
| `onChange` | `(value: CountryIso2) => void` | A callback function that is invoked when a country is selected. | Yes | |
|
|
11
|
+
| `name` | `string` | The name attribute for the input, used for integration with `react-hook-form`. | Yes | |
|
|
12
|
+
| `sx` | `SxProps` | Custom styles to be applied to the root element. | No | `{}` |
|
|
13
|
+
| `showDialCode` | `boolean` | If `true`, the country's dial code will be displayed alongside its name in the list. | No | `false` |
|
|
16
14
|
|
|
17
15
|
## Basic Usage
|
|
18
16
|
|
|
19
|
-
|
|
17
|
+
To use the `CountrySelect` component, you must wrap it in a `FormProvider` from `react-hook-form`. The component's state should be managed through the form context.
|
|
20
18
|
|
|
21
|
-
```
|
|
22
|
-
import { useState } from 'react';
|
|
19
|
+
```tsx Basic CountrySelect Example icon=logos:react
|
|
23
20
|
import { FormProvider, useForm } from 'react-hook-form';
|
|
24
|
-
import {
|
|
25
|
-
import
|
|
21
|
+
import { Box, Button, Typography } from '@mui/material';
|
|
22
|
+
import CountrySelect from '@blocklet/payment-react/src/components/country-select'; // Adjust path as needed
|
|
26
23
|
import type { CountryIso2 } from 'react-international-phone';
|
|
27
24
|
|
|
28
|
-
function BasicCountrySelect() {
|
|
29
|
-
const
|
|
30
|
-
const methods = useForm({
|
|
25
|
+
export default function BasicCountrySelect() {
|
|
26
|
+
const methods = useForm<{ country: CountryIso2 }>({
|
|
31
27
|
defaultValues: {
|
|
32
28
|
country: 'us',
|
|
33
29
|
},
|
|
34
30
|
});
|
|
35
31
|
|
|
36
|
-
const
|
|
37
|
-
|
|
38
|
-
|
|
32
|
+
const { handleSubmit, watch, setValue } = methods;
|
|
33
|
+
const countryValue = watch('country'); // Watch for changes to update the controlled component
|
|
34
|
+
|
|
35
|
+
const onSubmit = (data: { country: CountryIso2 }) => {
|
|
36
|
+
alert(`Form submitted with country: ${data.country}`);
|
|
39
37
|
};
|
|
40
38
|
|
|
41
39
|
return (
|
|
42
40
|
<FormProvider {...methods}>
|
|
43
|
-
<
|
|
44
|
-
<
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
41
|
+
<form onSubmit={handleSubmit(onSubmit)}>
|
|
42
|
+
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 2, maxWidth: 400 }}>
|
|
43
|
+
<Typography variant="h6">Select Your Country</Typography>
|
|
44
|
+
<CountrySelect
|
|
45
|
+
name="country"
|
|
46
|
+
value={countryValue}
|
|
47
|
+
onChange={(newCountry) => {
|
|
48
|
+
setValue('country', newCountry, { shouldValidate: true });
|
|
49
|
+
}}
|
|
50
|
+
/>
|
|
51
|
+
<Button type="submit" variant="contained">
|
|
52
|
+
Submit
|
|
53
|
+
</Button>
|
|
54
|
+
<Typography>
|
|
55
|
+
Current form value: {countryValue}
|
|
56
|
+
</Typography>
|
|
57
|
+
</Box>
|
|
58
|
+
</form>
|
|
50
59
|
</FormProvider>
|
|
51
60
|
);
|
|
52
61
|
}
|
|
53
|
-
|
|
54
|
-
export default BasicCountrySelect;
|
|
55
62
|
```
|
|
56
63
|
|
|
57
64
|
## Features
|
|
58
65
|
|
|
59
|
-
### Search and
|
|
60
|
-
The
|
|
66
|
+
### Search and Filter
|
|
67
|
+
The component includes a search bar at the top of the dropdown list, allowing users to quickly find a country by its name, ISO2 code, or dial code. For example, searching for "+1" will show the United States and Canada.
|
|
61
68
|
|
|
62
|
-
### Responsive
|
|
63
|
-
|
|
69
|
+
### Responsive UI
|
|
70
|
+
On desktop devices, `CountrySelect` renders as a standard dropdown menu. On mobile, it transforms into a full-width dialog that slides up from the bottom of the screen, providing an optimized user experience on smaller devices.
|
|
64
71
|
|
|
65
72
|
### Keyboard Accessibility
|
|
66
|
-
`CountrySelect`
|
|
67
|
-
- **ArrowDown / ArrowUp / Tab**: Navigate through the list of countries.
|
|
68
|
-
- **Enter**: Select the currently focused country and close the dropdown.
|
|
69
|
-
- **Escape**: Close the dropdown without making a selection.
|
|
73
|
+
`CountrySelect` supports full keyboard navigation. Users can use the `ArrowUp` and `ArrowDown` keys to navigate the list, `Enter` to select a country, and `Escape` to close the dropdown. `Tab` and `Shift+Tab` also cycle through the list items.
|
|
70
74
|
|
|
71
|
-
|
|
75
|
+
### Form Integration
|
|
76
|
+
Designed to work seamlessly with `react-hook-form`. It automatically updates the form state when a selection is made, requiring it to be wrapped within a `FormProvider`.
|
|
72
77
|
|
|
73
|
-
|
|
78
|
+
## Advanced Usage
|
|
74
79
|
|
|
75
|
-
|
|
80
|
+
### Displaying Dial Codes
|
|
76
81
|
|
|
77
|
-
|
|
78
|
-
|
|
82
|
+
You can display the telephone dial code for each country in the list by setting the `showDialCode` prop to `true`.
|
|
83
|
+
|
|
84
|
+
```tsx CountrySelect with Dial Code icon=logos:react
|
|
79
85
|
import { FormProvider, useForm } from 'react-hook-form';
|
|
80
|
-
import {
|
|
81
|
-
import
|
|
86
|
+
import { Box, Button } from '@mui/material';
|
|
87
|
+
import CountrySelect from '@blocklet/payment-react/src/components/country-select'; // Adjust path as needed
|
|
82
88
|
import type { CountryIso2 } from 'react-international-phone';
|
|
83
89
|
|
|
84
|
-
function CountrySelectWithDialCode() {
|
|
85
|
-
const
|
|
86
|
-
|
|
90
|
+
export default function CountrySelectWithDialCode() {
|
|
91
|
+
const methods = useForm<{ country: CountryIso2 }>({
|
|
92
|
+
defaultValues: {
|
|
93
|
+
country: 'gb',
|
|
94
|
+
},
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
const { handleSubmit, watch, setValue } = methods;
|
|
98
|
+
const countryValue = watch('country');
|
|
99
|
+
|
|
100
|
+
const onSubmit = (data: { country: CountryIso2 }) => {
|
|
101
|
+
alert(`Form submitted with country: ${data.country}`);
|
|
102
|
+
};
|
|
87
103
|
|
|
88
104
|
return (
|
|
89
105
|
<FormProvider {...methods}>
|
|
90
|
-
<
|
|
91
|
-
<
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
106
|
+
<form onSubmit={handleSubmit(onSubmit)}>
|
|
107
|
+
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 2, maxWidth: 400 }}>
|
|
108
|
+
<CountrySelect
|
|
109
|
+
name="country"
|
|
110
|
+
value={countryValue}
|
|
111
|
+
onChange={(newCountry) => setValue('country', newCountry)}
|
|
112
|
+
showDialCode={true}
|
|
113
|
+
/>
|
|
114
|
+
<Button type="submit" variant="contained">
|
|
115
|
+
Submit
|
|
116
|
+
</Button>
|
|
117
|
+
</Box>
|
|
118
|
+
</form>
|
|
98
119
|
</FormProvider>
|
|
99
120
|
);
|
|
100
121
|
}
|
|
101
|
-
|
|
102
|
-
export default CountrySelectWithDialCode;
|
|
103
122
|
```
|
|
104
123
|
|
|
105
|
-
|
|
124
|
+
## Next Steps
|
|
125
|
+
|
|
126
|
+
This component is a building block for more complex form elements. See how it's integrated into the [PhoneInput](./components-ui-form-elements-phone-input.md) component to create a complete international phone number field.
|
|
@@ -1,124 +1,108 @@
|
|
|
1
1
|
# CurrencySelector
|
|
2
2
|
|
|
3
|
-
The `CurrencySelector` component
|
|
4
|
-
|
|
5
|
-
This component is a form element designed to be integrated into broader payment flows, such as within a `CheckoutForm` or a custom payment UI.
|
|
3
|
+
The `CurrencySelector` component provides a user-friendly interface for selecting a payment currency from a list of available options. It displays each currency with its logo, symbol, and associated payment method, making it easy for users to make a choice. This component is typically used within a larger payment or checkout form.
|
|
6
4
|
|
|
7
5
|
## Props
|
|
8
6
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
|
12
|
-
|
|
|
13
|
-
| `
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
id: string; // ID of the associated payment method or network
|
|
29
|
-
name: string; // Display name of the associated payment method or network (e.g., "TRON Network")
|
|
7
|
+
| Prop | Type | Description | Required |
|
|
8
|
+
| :----------- | :------------------------------------------------------- | :---------------------------------------------------------------------------------------------------------------- | :------- |
|
|
9
|
+
| `value` | `string` | The ID of the currently selected currency. This should be managed by the parent component's state. | Yes |
|
|
10
|
+
| `currencies` | `TPaymentCurrency[]` | An array of available currency objects to display as selectable options. | Yes |
|
|
11
|
+
| `onChange` | `(currencyId: string, methodId?: string) => void` | Callback function that is executed when a user selects a currency. It receives the new currency ID and the optional associated payment method ID. | Yes |
|
|
12
|
+
|
|
13
|
+
### TPaymentCurrency Type
|
|
14
|
+
|
|
15
|
+
The `currencies` prop expects an array of objects with the following structure:
|
|
16
|
+
|
|
17
|
+
```typescript TPaymentCurrency icon=mdi:code-json
|
|
18
|
+
export type TPaymentCurrency = {
|
|
19
|
+
id: string; // Unique identifier for the currency option
|
|
20
|
+
logo: string; // URL for the currency/method logo
|
|
21
|
+
name: string; // Display name for the currency
|
|
22
|
+
symbol: string; // Currency symbol (e.g., '$', '€')
|
|
23
|
+
method: { // Associated payment method
|
|
24
|
+
id: string;
|
|
25
|
+
name: string;
|
|
30
26
|
};
|
|
31
|
-
}
|
|
27
|
+
};
|
|
32
28
|
```
|
|
33
29
|
|
|
34
|
-
## Example
|
|
30
|
+
## Usage Example
|
|
35
31
|
|
|
36
|
-
|
|
32
|
+
Here is a basic example of how to implement the `CurrencySelector` component. You need to manage the selected currency's state in the parent component and provide a list of available currencies.
|
|
37
33
|
|
|
38
|
-
Note that
|
|
34
|
+
Note that `@blocklet/payment-react` components are designed to work within a `PaymentProvider`, which supplies the necessary theme and context. Ensure your component is wrapped accordingly.
|
|
39
35
|
|
|
40
|
-
```tsx
|
|
36
|
+
```jsx MyCurrencySelectorComponent.tsx icon=logos:react
|
|
41
37
|
import React, { useState } from 'react';
|
|
42
|
-
import {
|
|
43
|
-
import {
|
|
38
|
+
import { PaymentProvider } from '@blocklet/payment-react';
|
|
39
|
+
import { CurrencySelector } from '@blocklet/payment-react/components';
|
|
44
40
|
import type { TPaymentCurrency } from '@blocklet/payment-types';
|
|
45
|
-
import {
|
|
41
|
+
import { useSessionContext } from '@/hooks/session-context'; // Adjust this import path to your project structure
|
|
46
42
|
|
|
47
|
-
// Mock data
|
|
43
|
+
// Mock data for demonstration purposes
|
|
48
44
|
const mockCurrencies: TPaymentCurrency[] = [
|
|
49
45
|
{
|
|
50
|
-
id: '
|
|
51
|
-
logo: 'https://
|
|
52
|
-
name: '
|
|
53
|
-
symbol: '
|
|
54
|
-
method: {
|
|
55
|
-
id: 'tron_network',
|
|
56
|
-
name: 'TRON Network',
|
|
57
|
-
},
|
|
58
|
-
},
|
|
59
|
-
{
|
|
60
|
-
id: 'btc_native',
|
|
61
|
-
logo: 'https://cdn.jsdelivr.net/gh/atomiclabs/cryptocurrency-icons@1a63530be6e374711a8554f31b17e4cb92c25661/svg/color/btc.svg',
|
|
62
|
-
name: 'Bitcoin',
|
|
63
|
-
symbol: 'BTC',
|
|
46
|
+
id: 'usd_xxxxxx',
|
|
47
|
+
logo: 'https://path-to-your/usd-logo.png',
|
|
48
|
+
name: 'US Dollar',
|
|
49
|
+
symbol: 'USD',
|
|
64
50
|
method: {
|
|
65
|
-
id: '
|
|
66
|
-
name: '
|
|
51
|
+
id: 'stripe_yyyyy',
|
|
52
|
+
name: 'Stripe',
|
|
67
53
|
},
|
|
68
54
|
},
|
|
69
55
|
{
|
|
70
|
-
id: '
|
|
71
|
-
logo: 'https://
|
|
56
|
+
id: 'eth_zzzzzz',
|
|
57
|
+
logo: 'https://path-to-your/eth-logo.png',
|
|
72
58
|
name: 'Ethereum',
|
|
73
59
|
symbol: 'ETH',
|
|
74
60
|
method: {
|
|
75
|
-
id: '
|
|
76
|
-
name: '
|
|
61
|
+
id: 'crypto_aaaaa',
|
|
62
|
+
name: 'Crypto Payment',
|
|
77
63
|
},
|
|
78
64
|
},
|
|
79
65
|
];
|
|
80
66
|
|
|
81
|
-
function
|
|
82
|
-
const [
|
|
67
|
+
function MyCurrencySelectorComponent() {
|
|
68
|
+
const [selectedCurrency, setSelectedCurrency] = useState<string>(mockCurrencies[0].id);
|
|
83
69
|
|
|
84
70
|
const handleCurrencyChange = (currencyId: string, methodId?: string) => {
|
|
85
|
-
console.log(`Selected
|
|
86
|
-
|
|
71
|
+
console.log(`Selected currency: ${currencyId}, Method: ${methodId}`);
|
|
72
|
+
setSelectedCurrency(currencyId);
|
|
87
73
|
};
|
|
88
74
|
|
|
89
75
|
return (
|
|
90
|
-
<
|
|
91
|
-
<
|
|
92
|
-
Select Your Payment Currency
|
|
93
|
-
</Typography>
|
|
76
|
+
<div>
|
|
77
|
+
<h3>Select a Currency</h3>
|
|
94
78
|
<CurrencySelector
|
|
95
|
-
value={
|
|
79
|
+
value={selectedCurrency}
|
|
96
80
|
currencies={mockCurrencies}
|
|
97
81
|
onChange={handleCurrencyChange}
|
|
98
82
|
/>
|
|
99
|
-
|
|
100
|
-
Current Selection: <strong>{selectedCurrencyId}</strong>
|
|
101
|
-
</Typography>
|
|
102
|
-
</Box>
|
|
83
|
+
</div>
|
|
103
84
|
);
|
|
104
85
|
}
|
|
105
86
|
|
|
106
|
-
//
|
|
107
|
-
export default function
|
|
87
|
+
// Your App's entry point
|
|
88
|
+
export default function App() {
|
|
108
89
|
const { session, connectApi } = useSessionContext();
|
|
109
90
|
|
|
110
|
-
// PaymentProvider requires session and connectApi.
|
|
111
|
-
// Ensure these are available before rendering.
|
|
112
91
|
if (!session || !connectApi) {
|
|
113
|
-
return <
|
|
92
|
+
return <div>Loading...</div>;
|
|
114
93
|
}
|
|
115
94
|
|
|
116
95
|
return (
|
|
117
96
|
<PaymentProvider session={session} connectApi={connectApi}>
|
|
118
|
-
<
|
|
97
|
+
<MyCurrencySelectorComponent />
|
|
119
98
|
</PaymentProvider>
|
|
120
99
|
);
|
|
121
100
|
}
|
|
122
101
|
```
|
|
123
102
|
|
|
124
|
-
In this example
|
|
103
|
+
In this example:
|
|
104
|
+
1. We define a `mockCurrencies` array that follows the `TPaymentCurrency` structure.
|
|
105
|
+
2. The `MyCurrencySelectorComponent` uses the `useState` hook to keep track of the `selectedCurrency`.
|
|
106
|
+
3. The `handleCurrencyChange` function updates the state when a new currency is selected and logs the new values.
|
|
107
|
+
4. The `CurrencySelector` is a controlled component, with its `value` and `onChange` props managed by the parent's state.
|
|
108
|
+
5. The entire component is wrapped in `PaymentProvider` to ensure proper rendering and access to the Material-UI theme used for styling.
|
|
@@ -1,160 +1,138 @@
|
|
|
1
1
|
# PhoneInput
|
|
2
2
|
|
|
3
|
-
The `PhoneInput` component provides a user-friendly
|
|
3
|
+
The `PhoneInput` component provides a user-friendly, international phone number input field. It integrates seamlessly with `react-hook-form` and includes a searchable country selector with flags and dial codes, automatic number formatting, and powerful validation powered by `google-libphonenumber`.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
This component is designed to work in tandem with other form elements, such as the [`CountrySelect`](./components-ui-form-elements-country-select.md) or [`AddressForm`](./components-ui-form-elements-address-form.md), by synchronizing its selected country with a shared form field.
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
7
|
+
## How It Works
|
|
8
|
+
|
|
9
|
+
The component leverages the `react-international-phone` library for its core functionality and wraps it to provide deep integration with `react-hook-form` and Material-UI. When a user selects a country or types a number, the component updates the corresponding fields in the form state. It also automatically updates its country selection when another component modifies the linked country field in the form.
|
|
10
|
+
|
|
11
|
+
```d2
|
|
12
|
+
direction: down
|
|
13
|
+
|
|
14
|
+
User: {
|
|
15
|
+
shape: c4-person
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
Form-State: {
|
|
19
|
+
label: "React Hook Form State"
|
|
20
|
+
shape: cylinder
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
PhoneInput-Component: {
|
|
24
|
+
label: "PhoneInput Component"
|
|
25
|
+
|
|
26
|
+
CountrySelect: {
|
|
27
|
+
label: "CountrySelect"
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
Phone-Field: {
|
|
31
|
+
label: "Phone Text Field"
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
User -> PhoneInput-Component.CountrySelect: "1. Selects a country"
|
|
36
|
+
PhoneInput-Component.CountrySelect -> Form-State: "2. Updates 'countryFieldName' value"
|
|
37
|
+
|
|
38
|
+
User -> PhoneInput-Component.Phone-Field: "3. Enters phone number"
|
|
39
|
+
PhoneInput-Component.Phone-Field -> Form-State: "4. Updates 'name' value (phone number)"
|
|
40
|
+
|
|
41
|
+
Form-State -> PhoneInput-Component: "5. Synchronizes state"
|
|
42
|
+
```
|
|
12
43
|
|
|
13
44
|
## Props
|
|
14
45
|
|
|
15
|
-
The `PhoneInput` component accepts all standard
|
|
46
|
+
The `PhoneInput` component accepts all standard Material-UI `TextField` props, in addition to the following specific props:
|
|
16
47
|
|
|
17
|
-
| Prop | Type |
|
|
18
|
-
|
|
19
|
-
| `name` | `string` |
|
|
20
|
-
| `countryFieldName` | `string` | The name of the form field that stores the selected country's
|
|
21
|
-
| `...rest` | `TextFieldProps` | Any other props accepted by the Material-UI `TextField` component, such as `label`, `placeholder`, `required`, `helperText`, and `error`. | - |
|
|
48
|
+
| Prop | Type | Required | Default | Description |
|
|
49
|
+
|---|---|---|---|---|
|
|
50
|
+
| `name` | `string` | Yes | - | The name of the field to register with `react-hook-form` for the phone number. |
|
|
51
|
+
| `countryFieldName` | `string` | No | `'billing_address.country'` | The name of the form field that stores the selected country's ISO2 code. This allows the phone input's country to be synchronized with other parts of your form, such as an address component. |
|
|
22
52
|
|
|
23
53
|
## Usage
|
|
24
54
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
55
|
+
To use the `PhoneInput` component, you must wrap it in a `FormProvider` from `react-hook-form`. The following example demonstrates a basic implementation with validation.
|
|
56
|
+
|
|
57
|
+
First, you need the asynchronous validation function provided by the library.
|
|
58
|
+
|
|
59
|
+
```javascript phone-validator.js icon=logos:javascript
|
|
60
|
+
// src/libs/phone-validator.js
|
|
61
|
+
import { getPhoneUtil } from '@blocklet/payment-react/libs/phone-validator';
|
|
62
|
+
|
|
63
|
+
export const validatePhoneNumber = async (phoneNumber) => {
|
|
64
|
+
if (!phoneNumber) return true;
|
|
65
|
+
try {
|
|
66
|
+
const util = await getPhoneUtil();
|
|
67
|
+
const parsed = util.parseAndKeepRawInput(phoneNumber);
|
|
68
|
+
return util.isValidNumber(parsed);
|
|
69
|
+
} catch (err) {
|
|
70
|
+
console.error('Phone validation error:', err);
|
|
71
|
+
// Fallback to a simple regex if lib fails to load
|
|
72
|
+
const pattern = /^[+]?[(]?[0-9]{3}[)]?[-\s.]?[0-9]{3}[-\s.]?[0-9]{4,6}$/im;
|
|
73
|
+
return pattern.test(phoneNumber) || 'Invalid phone number';
|
|
74
|
+
}
|
|
75
|
+
};
|
|
76
|
+
```
|
|
28
77
|
|
|
29
|
-
|
|
78
|
+
Now, you can use this validator in your form component.
|
|
30
79
|
|
|
31
|
-
```tsx
|
|
80
|
+
```jsx MyPaymentForm.tsx icon=logos:react
|
|
32
81
|
import { FormProvider, useForm } from 'react-hook-form';
|
|
33
|
-
import { Button,
|
|
34
|
-
import PhoneInput from '@blocklet/payment-react
|
|
35
|
-
import
|
|
36
|
-
import { validatePhoneNumber } from '@blocklet/payment-react/libs/phone-validator';
|
|
82
|
+
import { Button, Box } from '@mui/material';
|
|
83
|
+
import { PhoneInput } from '@blocklet/payment-react';
|
|
84
|
+
import { validatePhoneNumber } from '../libs/phone-validator'; // Adjust path as needed
|
|
37
85
|
|
|
38
|
-
export default function
|
|
86
|
+
export default function MyPaymentForm() {
|
|
39
87
|
const methods = useForm({
|
|
40
|
-
mode: 'onBlur',
|
|
88
|
+
mode: 'onBlur',
|
|
41
89
|
defaultValues: {
|
|
42
90
|
phone: '',
|
|
43
91
|
'billing_address.country': 'us', // Default country
|
|
44
92
|
},
|
|
45
93
|
});
|
|
46
94
|
|
|
47
|
-
const { handleSubmit, formState: { errors }, watch, setValue, register } = methods;
|
|
48
|
-
|
|
49
95
|
const onSubmit = (data) => {
|
|
50
96
|
alert(JSON.stringify(data, null, 2));
|
|
51
97
|
};
|
|
52
98
|
|
|
53
99
|
return (
|
|
54
100
|
<FormProvider {...methods}>
|
|
55
|
-
<form onSubmit={handleSubmit(onSubmit)}>
|
|
56
|
-
<
|
|
57
|
-
<CountrySelect
|
|
58
|
-
name="billing_address.country"
|
|
59
|
-
value={watch('billing_address.country')}
|
|
60
|
-
onChange={(country) => setValue('billing_address.country', country, { shouldValidate: true })}
|
|
61
|
-
/>
|
|
101
|
+
<form onSubmit={methods.handleSubmit(onSubmit)}>
|
|
102
|
+
<Box display="flex" flexDirection="column" gap={2}>
|
|
62
103
|
<PhoneInput
|
|
63
104
|
label="Phone Number"
|
|
64
105
|
name="phone"
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
106
|
+
// Link to the country field in the form state
|
|
107
|
+
countryFieldName="billing_address.country"
|
|
108
|
+
// Add validation rules
|
|
109
|
+
rules={{
|
|
68
110
|
validate: async (value) => {
|
|
69
111
|
const isValid = await validatePhoneNumber(value);
|
|
70
|
-
return isValid || 'Please enter a valid phone number';
|
|
112
|
+
return isValid || 'Please enter a valid phone number.';
|
|
71
113
|
},
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
helperText={errors.phone?.message?.toString()}
|
|
114
|
+
}}
|
|
115
|
+
fullWidth
|
|
75
116
|
/>
|
|
76
117
|
<Button type="submit" variant="contained">
|
|
77
118
|
Submit
|
|
78
119
|
</Button>
|
|
79
|
-
</
|
|
120
|
+
</Box>
|
|
80
121
|
</form>
|
|
81
122
|
</FormProvider>
|
|
82
123
|
);
|
|
83
124
|
}
|
|
84
125
|
```
|
|
85
126
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
The key feature of `PhoneInput` is its ability to synchronize with an external country field. This is particularly useful in forms like an [AddressForm](./components-ui-form-elements-address-form.md) where the phone number's country should match the billing address country.
|
|
89
|
-
|
|
90
|
-
- **Default Behavior**: By default, it listens to and updates a form field named `billing_address.country`.
|
|
91
|
-
- **Customization**: You can specify a different field name by passing the `countryFieldName` prop.
|
|
127
|
+
### Explanation
|
|
92
128
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
129
|
+
1. **`FormProvider`**: The entire form, including the `PhoneInput`, is wrapped in a `FormProvider` to provide the necessary form context.
|
|
130
|
+
2. **`name="phone"`**: This registers the input field under the name `phone` in the form data.
|
|
131
|
+
3. **`countryFieldName="billing_address.country"`**: This tells `PhoneInput` to both read from and write to the `billing_address.country` field in the form's state. This is how it stays synchronized. The default value for this field is set to `'us'` in `useForm`.
|
|
132
|
+
4. **`rules`**: We pass an asynchronous `validate` function to the `rules` prop. `react-hook-form` will await this function to resolve during validation. The `validatePhoneNumber` utility is called to perform the check.
|
|
96
133
|
|
|
97
|
-
|
|
134
|
+
## Integration with AddressForm
|
|
98
135
|
|
|
99
|
-
|
|
100
|
-
direction: down
|
|
101
|
-
|
|
102
|
-
User: "User"
|
|
103
|
-
|
|
104
|
-
UI: {
|
|
105
|
-
shape: package
|
|
106
|
-
"PhoneInput": {
|
|
107
|
-
"Internal CountrySelect": "Country selector inside phone input"
|
|
108
|
-
}
|
|
109
|
-
"External CountrySelect": "Separate country selector for address"
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
FormState: "react-hook-form State" {
|
|
113
|
-
shape: cylinder
|
|
114
|
-
phone: "Phone number value"
|
|
115
|
-
"billing_address.country": "Country ISO code"
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
User -> UI."External CountrySelect": "Selects 'Canada'"
|
|
119
|
-
UI."External CountrySelect" -> FormState."billing_address.country": "Updates state to 'ca'"
|
|
120
|
-
FormState."billing_address.country" -> UI."PhoneInput"."Internal CountrySelect": "Syncs flag and dial code to Canada"
|
|
121
|
-
|
|
122
|
-
User -> UI."PhoneInput"."Internal CountrySelect": "Selects 'UK'"
|
|
123
|
-
UI."PhoneInput"."Internal CountrySelect" -> FormState."billing_address.country": "Updates state to 'gb'"
|
|
124
|
-
FormState."billing_address.country" -> UI."External CountrySelect": "Syncs selection to UK"
|
|
125
|
-
```
|
|
126
|
-
|
|
127
|
-
## Validation
|
|
128
|
-
|
|
129
|
-
Phone number validation is handled by the `validatePhoneNumber` utility. This function asynchronously loads the `google-libphonenumber` library to perform validation without impacting your application's initial load time. If the library fails to load, it falls back to a basic regex pattern for validation.
|
|
130
|
-
|
|
131
|
-
To apply validation, use the `validate` function in the rules object when registering the field with `react-hook-form`, as shown in the example above.
|
|
132
|
-
|
|
133
|
-
## Helper Utilities
|
|
134
|
-
|
|
135
|
-
### `formatPhone`
|
|
136
|
-
|
|
137
|
-
The library also exports a `formatPhone` utility function that can be used to standardize a phone number into the E.164 international format (e.g., `+12125552368`). This is useful for normalizing data before sending it to your backend.
|
|
138
|
-
|
|
139
|
-
**Parameters**
|
|
140
|
-
|
|
141
|
-
| Parameter | Type | Description | Default |
|
|
142
|
-
| --- | --- | --- | --- |
|
|
143
|
-
| `phoneNumber` | `string` | The original phone number string. | - |
|
|
144
|
-
| `defaultCountry` | `string` | The default country code (ISO 3166-1 alpha-2) to use if the number is ambiguous. | `'US'` |
|
|
145
|
-
|
|
146
|
-
**Example**
|
|
147
|
-
|
|
148
|
-
```javascript
|
|
149
|
-
import { formatPhone } from '@blocklet/payment-react/libs/phone-validator';
|
|
150
|
-
|
|
151
|
-
const rawNumber = '(541) 754-3010';
|
|
152
|
-
const formattedNumber = formatPhone(rawNumber, 'US');
|
|
153
|
-
// formattedNumber will be '+15417543010'
|
|
154
|
-
|
|
155
|
-
const rawUkNumber = '07700 900077';
|
|
156
|
-
const formattedUkNumber = formatPhone(rawUkNumber, 'GB');
|
|
157
|
-
// formattedUkNumber will be '+447700900077'
|
|
158
|
-
```
|
|
136
|
+
The true power of `PhoneInput` is revealed when used alongside an `AddressForm`. Since both components can be linked to the same country field (`billing_address.country` by default), changing the country in the `AddressForm` will automatically update the flag and dial code in the `PhoneInput`.
|
|
159
137
|
|
|
160
|
-
|
|
138
|
+
For a complete example of this integration, please see the documentation for [`AddressForm`](./components-ui-form-elements-address-form.md).
|