@afosto/checkout-ui 0.0.14 → 0.0.16
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/README.md +333 -1
- package/dist/CheckoutAddressFields/CheckoutAddressFields.d.ts.map +1 -1
- package/dist/CheckoutAddressFields/CheckoutAddressFields.js +59 -57
- package/dist/CheckoutAgreementOptionCondition/CheckoutAgreementOptionCondition.d.ts +1 -1
- package/dist/CheckoutAgreementOptionCondition/CheckoutAgreementOptionCondition.js +23 -23
- package/dist/CheckoutAgreementsTimeline/CheckoutAgreementsTimeline.js +10 -10
- package/dist/CheckoutShippingMethodSection/CheckoutShippingMethodSection.d.ts +1 -1
- package/dist/CheckoutShippingMethodSection/CheckoutShippingMethodSection.d.ts.map +1 -1
- package/dist/CheckoutShippingMethodsField/CheckoutShippingMethodsField.d.ts +1 -1
- package/dist/CheckoutShippingMethodsField/CheckoutShippingMethodsField.d.ts.map +1 -1
- package/dist/CheckoutShippingMethodsField/CheckoutShippingMethodsField.js +78 -77
- package/dist/constants/paymentMethodIconMapping.d.ts +9 -0
- package/dist/constants/paymentMethodIconMapping.d.ts.map +1 -1
- package/dist/constants/paymentMethodIconMapping.js +76 -58
- package/dist/hooks/useCheckoutShippingMethods.d.ts +2 -1
- package/dist/hooks/useCheckoutShippingMethods.d.ts.map +1 -1
- package/dist/hooks/useCheckoutShippingMethods.js +110 -67
- package/dist/icons/index.js +178 -160
- package/dist/icons/paymentMethods/Alma.d.ts +3 -0
- package/dist/icons/paymentMethods/Alma.d.ts.map +1 -0
- package/dist/icons/paymentMethods/Alma.js +15 -0
- package/dist/icons/paymentMethods/Bancomat.d.ts +3 -0
- package/dist/icons/paymentMethods/Bancomat.d.ts.map +1 -0
- package/dist/icons/paymentMethods/Bancomat.js +15 -0
- package/dist/icons/paymentMethods/Bizum.d.ts +3 -0
- package/dist/icons/paymentMethods/Bizum.d.ts.map +1 -0
- package/dist/icons/paymentMethods/Bizum.js +28 -0
- package/dist/icons/paymentMethods/Blik.d.ts +3 -0
- package/dist/icons/paymentMethods/Blik.d.ts.map +1 -0
- package/dist/icons/paymentMethods/Blik.js +59 -0
- package/dist/icons/paymentMethods/ClickToPay.d.ts +3 -0
- package/dist/icons/paymentMethods/ClickToPay.d.ts.map +1 -0
- package/dist/icons/paymentMethods/ClickToPay.js +35 -0
- package/dist/icons/paymentMethods/MBWay.d.ts +3 -0
- package/dist/icons/paymentMethods/MBWay.d.ts.map +1 -0
- package/dist/icons/paymentMethods/MBWay.js +65 -0
- package/dist/icons/paymentMethods/MobilePay.d.ts +3 -0
- package/dist/icons/paymentMethods/MobilePay.d.ts.map +1 -0
- package/dist/icons/paymentMethods/MobilePay.js +103 -0
- package/dist/icons/paymentMethods/Satispay.d.ts +3 -0
- package/dist/icons/paymentMethods/Satispay.d.ts.map +1 -0
- package/dist/icons/paymentMethods/Satispay.js +15 -0
- package/dist/icons/paymentMethods/Vipps.d.ts +3 -0
- package/dist/icons/paymentMethods/Vipps.d.ts.map +1 -0
- package/dist/icons/paymentMethods/Vipps.js +15 -0
- package/dist/icons/paymentMethods/index.d.ts +9 -0
- package/dist/icons/paymentMethods/index.d.ts.map +1 -1
- package/dist/icons/paymentMethods/index.js +124 -106
- package/dist/index.js +356 -338
- package/dist/operations/cartOperations.d.ts.map +1 -1
- package/dist/operations/cartOperations.js +4 -6
- package/dist/operations/orderOperations.d.ts.map +1 -1
- package/dist/operations/orderOperations.js +4 -6
- package/dist/queries/getCartShippingMethodsQuery.d.ts +0 -1
- package/dist/queries/getCartShippingMethodsQuery.d.ts.map +1 -1
- package/dist/queries/getCartShippingMethodsQuery.js +6 -29
- package/dist/queries/getOrderShippingMethodsQuery.d.ts +0 -1
- package/dist/queries/getOrderShippingMethodsQuery.d.ts.map +1 -1
- package/dist/queries/getOrderShippingMethodsQuery.js +4 -27
- package/dist/types.d.ts +1 -1
- package/dist/types.d.ts.map +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -1 +1,333 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @afosto/checkout-ui
|
|
2
|
+
|
|
3
|
+
React components and state for building a checkout on the Afosto storefront API. The package contains a complete checkout with information, shipping, payment and review steps. Render it as a whole with `<Checkout />`, or compose your own checkout from the separate layout, form, sections, fields and hooks.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pnpm add @afosto/checkout-ui @afosto/storefront @emotion/react @emotion/styled
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Peer dependencies are `react` and `react-dom` 18, `@emotion/react` and `@emotion/styled`. The package ships ES modules only. The components and providers are marked `'use client'`, so in Next.js you render them from a client component.
|
|
12
|
+
|
|
13
|
+
## Quick start
|
|
14
|
+
|
|
15
|
+
A checkout needs three things:
|
|
16
|
+
|
|
17
|
+
1. A storefront client from `@afosto/storefront`, created with the storefront token of your channel.
|
|
18
|
+
2. The id of the cart the customer is checking out.
|
|
19
|
+
3. A `CheckoutProvider` that loads the cart, with a `Checkout` inside that renders it.
|
|
20
|
+
|
|
21
|
+
```tsx
|
|
22
|
+
'use client';
|
|
23
|
+
|
|
24
|
+
import { createStorefrontClient } from '@afosto/storefront';
|
|
25
|
+
import { Checkout, CheckoutProvider } from '@afosto/checkout-ui';
|
|
26
|
+
|
|
27
|
+
const storefront = createStorefrontClient({
|
|
28
|
+
storefrontToken: '<your storefront token>',
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
if (typeof window !== 'undefined') {
|
|
32
|
+
window.dataLayer = window.dataLayer || [];
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export const CheckoutPage = ({ cartId }: { cartId: string }) => (
|
|
36
|
+
<CheckoutProvider storefront={storefront} cartId={cartId}>
|
|
37
|
+
<Checkout />
|
|
38
|
+
</CheckoutProvider>
|
|
39
|
+
);
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
The `dataLayer` line is only needed if you want analytics events. See [Analytics](#analytics).
|
|
43
|
+
|
|
44
|
+
This renders the full checkout: a top bar with the logo, a stepper, the step forms, the order summary, sign-in for customers with an account and a message when the cart doesn't exist. Logo, colours, currency, locale and links come from the channel. You don't have to configure anything else for a working checkout.
|
|
45
|
+
|
|
46
|
+
## Cart flow and order flow
|
|
47
|
+
|
|
48
|
+
`CheckoutProvider` takes either a `cartId` or an `orderId`, never both. Passing both throws an error.
|
|
49
|
+
|
|
50
|
+
- With `cartId` the provider runs the cart flow (`flow: 'cart'`). The customer fills in the steps and the last step confirms the cart, which turns it into an order.
|
|
51
|
+
- With `orderId` the provider runs the order flow (`flow: 'order'`). It runs the same steps against an existing order, for example an order that still needs a payment.
|
|
52
|
+
|
|
53
|
+
```tsx
|
|
54
|
+
<CheckoutProvider storefront={storefront} orderId={orderId}>
|
|
55
|
+
<Checkout />
|
|
56
|
+
</CheckoutProvider>
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
After the last step the package redirects the browser. For a `pin` payment method it goes to `{channelLinks.checkout}/payment/execute?order_id=...`. Otherwise it follows the first proceeding option the API returns, usually the payment provider's page.
|
|
60
|
+
|
|
61
|
+
If `cartId` or `orderId` is empty, the checkout shows the not-found message.
|
|
62
|
+
|
|
63
|
+
## What the provider does
|
|
64
|
+
|
|
65
|
+
`CheckoutProvider` sets up everything the components need:
|
|
66
|
+
|
|
67
|
+
1. It fetches the channel through `storefront.getChannel()`. The channel supplies the locale, currency, logo, store name, branding colours, links (terms, privacy, my account) and the B2B/B2C and required-field options.
|
|
68
|
+
2. It merges those channel values with the `settings` prop. A value in `settings` always wins over the channel.
|
|
69
|
+
3. It wraps the tree in a MUI theme from `@afosto/components`, with the primary palette built from the channel's primary colour.
|
|
70
|
+
4. It creates a React Query client, unless you pass your own through `queryClient`.
|
|
71
|
+
5. It loads the cart or order and exposes all state and actions through the `useCheckout()` hook.
|
|
72
|
+
|
|
73
|
+
### Provider props
|
|
74
|
+
|
|
75
|
+
| Prop | Description |
|
|
76
|
+
| --- | --- |
|
|
77
|
+
| `storefront` | Required. A client from `createStorefrontClient()` in `@afosto/storefront`. |
|
|
78
|
+
| `cartId` / `orderId` | Required, pick one. Decides the flow. |
|
|
79
|
+
| `settings` | Overrides the channel settings. See [Settings](#settings). |
|
|
80
|
+
| `translations` | Text overrides per component. See [Translations](#translations). |
|
|
81
|
+
| `locale` | Overrides the channel locale. Falls back to `en`. |
|
|
82
|
+
| `theme` | Partial MUI theme, deep-merged over the Afosto theme. |
|
|
83
|
+
| `steps` | Replaces the default steps. See [Steps](#steps). |
|
|
84
|
+
| `operations` | Overrides or extends the API calls. See [Operations](#operations). |
|
|
85
|
+
| `analytics` | Overrides or extends the analytics events. See [Analytics](#analytics). |
|
|
86
|
+
| `createSummary` | Replaces the function that turns a cart or order into the summary data. |
|
|
87
|
+
| `expressPaymentMethodsComponentMapping` | Maps express payment method codes to button components. PayPal Express is built in. |
|
|
88
|
+
| `isLoading` | Delays fetching the channel and the cart until it is `false`. Use it while you are still resolving the cart id or token. |
|
|
89
|
+
| `queryClient` | Your own React Query client. |
|
|
90
|
+
|
|
91
|
+
### Settings
|
|
92
|
+
|
|
93
|
+
Commonly used fields of `settings` (type `CheckoutSettings`):
|
|
94
|
+
|
|
95
|
+
- `emailOptIn`: show the newsletter opt-in.
|
|
96
|
+
- `salutation`, `isSalutationRequired`: show and require the salutation field.
|
|
97
|
+
- `isPhoneNumberRequired`, `isCocNumberRequired`, `isVatNumberRequired`, `isSharedOrganisationRequired`: required fields. Default to the channel options.
|
|
98
|
+
- `isB2bExcluded`, `isB2cExcluded`: hide the business or consumer option. Default to the channel options.
|
|
99
|
+
- `isAccountEnabled`: show sign-in. Defaults to `true` when the channel has a my account link.
|
|
100
|
+
- `disableCouponModifications`: hide adding and removing coupons.
|
|
101
|
+
- `disableAnalytics`: stop all analytics events.
|
|
102
|
+
- `links`, `logo`, `storeName`, `currency`, `branding`: override what the channel returns.
|
|
103
|
+
|
|
104
|
+
```tsx
|
|
105
|
+
<CheckoutProvider
|
|
106
|
+
storefront={storefront}
|
|
107
|
+
cartId={cartId}
|
|
108
|
+
settings={{
|
|
109
|
+
emailOptIn: true,
|
|
110
|
+
salutation: true,
|
|
111
|
+
isPhoneNumberRequired: true,
|
|
112
|
+
links: { termsConditions: 'https://example.com/terms' },
|
|
113
|
+
}}
|
|
114
|
+
>
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
## Steps
|
|
118
|
+
|
|
119
|
+
The package exports four default steps:
|
|
120
|
+
|
|
121
|
+
| Key | Export | Contents |
|
|
122
|
+
| --- | --- | --- |
|
|
123
|
+
| `information` | `informationStep` | Sign in, account selection, contact details, business details, shipping and billing address. |
|
|
124
|
+
| `shipping` | `shippingStep` | Shipping method and pickup point. |
|
|
125
|
+
| `payment` | `paymentStep` | Payment method, issuer, terminal and agreement (down payment, post paid). |
|
|
126
|
+
| `review` | `reviewStep` | Overview, comment and confirm. |
|
|
127
|
+
|
|
128
|
+
A step is a plain object of type `CheckoutStep`:
|
|
129
|
+
|
|
130
|
+
- `key`, `label`: identifier and stepper label.
|
|
131
|
+
- `component`: the React component rendered for the step.
|
|
132
|
+
- `defaultValues`: the starting form values, or a function that returns them.
|
|
133
|
+
- `initialize(data, responseData, context)`: builds the form values from the loaded cart or order.
|
|
134
|
+
- `validationSchema(context)`: a `yup` schema for the step's values.
|
|
135
|
+
- `isComplete(values, context)`: decides whether the customer can skip past this step. On load the checkout opens on the first step that is not complete.
|
|
136
|
+
- `submit(values, formData, context)`: runs when the customer clicks next. This is where the step sends its data to the API.
|
|
137
|
+
- `onEvent` (optional): reacts to events such as `signedIn`, `accountSelected` and `clearSession`.
|
|
138
|
+
- `reviewTargets`, `getSubStepForReviewTarget` (optional): let the review step link back to a field in this step.
|
|
139
|
+
|
|
140
|
+
To change the flow, pass your own array. You can reuse the defaults:
|
|
141
|
+
|
|
142
|
+
```tsx
|
|
143
|
+
import { informationStep, shippingStep, paymentStep, reviewStep } from '@afosto/checkout-ui';
|
|
144
|
+
|
|
145
|
+
<CheckoutProvider
|
|
146
|
+
storefront={storefront}
|
|
147
|
+
cartId={cartId}
|
|
148
|
+
steps={[informationStep, { ...shippingStep, label: 'Delivery' }, paymentStep, reviewStep]}
|
|
149
|
+
>
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
An empty array falls back to the defaults.
|
|
153
|
+
|
|
154
|
+
## Operations
|
|
155
|
+
|
|
156
|
+
Every API call goes through an operation. An operation has four functions:
|
|
157
|
+
|
|
158
|
+
```ts
|
|
159
|
+
{
|
|
160
|
+
queryFn: (params, context) => Promise<Response>; // calls the API
|
|
161
|
+
formatter: (response, context) => FormattedResponse; // reshapes the response
|
|
162
|
+
success: (data, context) => void; // updates checkout state, tracks analytics
|
|
163
|
+
error: (error) => void;
|
|
164
|
+
}
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
The package exports the defaults as `cartOperations`, `orderOperations` (same keys, for the order flow) and `authOperations`. Examples of keys are `initial`, `addCustomer`, `addInformation`, `addShippingMethod`, `addPaymentMethod`, `addCoupon`, `checkout`, `signIn` and `signUp`.
|
|
168
|
+
|
|
169
|
+
The `operations` prop merges per operation and per function. You only pass what you want to change. The other functions keep their default:
|
|
170
|
+
|
|
171
|
+
```tsx
|
|
172
|
+
<CheckoutProvider
|
|
173
|
+
storefront={storefront}
|
|
174
|
+
cartId={cartId}
|
|
175
|
+
operations={{
|
|
176
|
+
addCustomer: {
|
|
177
|
+
error: (error) => reportError(error),
|
|
178
|
+
},
|
|
179
|
+
}}
|
|
180
|
+
>
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
The `context` argument holds the storefront client, settings, the current flow, `defaultOperations` (so you can call the original from your override), `setSummary`, `track` and the rest of the checkout state.
|
|
184
|
+
|
|
185
|
+
Keys that don't exist yet are added as custom operations. The package does not call them by itself. You can read them from `useCheckout().operations` in your own components.
|
|
186
|
+
|
|
187
|
+
## Analytics
|
|
188
|
+
|
|
189
|
+
The package pushes GA4 ecommerce events to `window.dataLayer`. Events only fire when `window.dataLayer` exists, so create it before the checkout renders:
|
|
190
|
+
|
|
191
|
+
```ts
|
|
192
|
+
window.dataLayer = window.dataLayer || [];
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
Default events:
|
|
196
|
+
|
|
197
|
+
| Key | dataLayer event |
|
|
198
|
+
| --- | --- |
|
|
199
|
+
| `beginCheckout` | `begin_checkout` |
|
|
200
|
+
| `checkoutStep` | `checkout_step` |
|
|
201
|
+
| `addShippingInfo` | `add_shipping_info` |
|
|
202
|
+
| `addPaymentInfo` | `add_payment_info` |
|
|
203
|
+
| `signIn` | `login` |
|
|
204
|
+
| `signUp` | `sign_up` |
|
|
205
|
+
|
|
206
|
+
Each event has an optional `formatter(eventData, context)` and a `send(formattedData, rawData, context)`. The `analytics` prop merges per event, like operations. Use it to change the payload or to send events somewhere else.
|
|
207
|
+
|
|
208
|
+
`stepSubmitted` fires after every step submit, but has no default `send`. Add one if you need it:
|
|
209
|
+
|
|
210
|
+
```tsx
|
|
211
|
+
analytics={{
|
|
212
|
+
stepSubmitted: {
|
|
213
|
+
send: (eventData, rawData, context) => {
|
|
214
|
+
window.dataLayer?.push({ event: 'step_submitted', step: rawData.step.key });
|
|
215
|
+
},
|
|
216
|
+
},
|
|
217
|
+
}}
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
Set `settings.disableAnalytics` to turn off all events.
|
|
221
|
+
|
|
222
|
+
## Translations
|
|
223
|
+
|
|
224
|
+
Every component has English default texts. The `translations` prop on the provider overrides them per component. The keys are listed in the `CheckoutTranslations` type, for example `summary`, `addressFields`, `shippingMethodsField`, `paymentMethodSection` and `validation`.
|
|
225
|
+
|
|
226
|
+
```tsx
|
|
227
|
+
translations={{
|
|
228
|
+
summary: { title: 'Overzicht', subtotal: 'Subtotaal' },
|
|
229
|
+
validation: { required: 'Dit veld is verplicht' },
|
|
230
|
+
}}
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
The package only ships English. For another language, pass all texts through `translations`.
|
|
234
|
+
|
|
235
|
+
## Customising the UI
|
|
236
|
+
|
|
237
|
+
### Slots
|
|
238
|
+
|
|
239
|
+
`Checkout`, `CheckoutLayout` and most larger components accept `slots` and `slotProps`. A slot replaces a part with your own component. `slotProps` passes extra props to that part. `slotProps` can also be a function that gets the owner state (for example `{ flow, notFound }`) and returns props.
|
|
240
|
+
|
|
241
|
+
```tsx
|
|
242
|
+
<Checkout
|
|
243
|
+
slots={{ notFoundFeedback: MyNotFoundPage }}
|
|
244
|
+
slotProps={{
|
|
245
|
+
layout: { hideTopBar: true },
|
|
246
|
+
summary: { hideTitle: true },
|
|
247
|
+
}}
|
|
248
|
+
/>
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
### Theme
|
|
252
|
+
|
|
253
|
+
Pass `theme` to override the MUI theme. The primary palette comes from `settings.branding.colors.primary` (or the channel branding) and is overridden by `theme.palette.primary`.
|
|
254
|
+
|
|
255
|
+
```tsx
|
|
256
|
+
<CheckoutProvider
|
|
257
|
+
storefront={storefront}
|
|
258
|
+
cartId={cartId}
|
|
259
|
+
theme={{ palette: { primary: { main: '#0b5cff' } } }}
|
|
260
|
+
>
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
### Composing your own checkout
|
|
264
|
+
|
|
265
|
+
Every part of `<Checkout />` is exported. Inside a `CheckoutProvider` you can put them together yourself:
|
|
266
|
+
|
|
267
|
+
```tsx
|
|
268
|
+
import {
|
|
269
|
+
CheckoutForm,
|
|
270
|
+
CheckoutLayout,
|
|
271
|
+
CheckoutProvider,
|
|
272
|
+
CheckoutSignIn,
|
|
273
|
+
useCheckout,
|
|
274
|
+
} from '@afosto/checkout-ui';
|
|
275
|
+
|
|
276
|
+
const MyCheckout = () => {
|
|
277
|
+
const { showSignIn } = useCheckout();
|
|
278
|
+
|
|
279
|
+
return (
|
|
280
|
+
<CheckoutLayout>
|
|
281
|
+
<MyIntroText />
|
|
282
|
+
<CheckoutForm hideForm={showSignIn} />
|
|
283
|
+
{showSignIn && <CheckoutSignIn />}
|
|
284
|
+
</CheckoutLayout>
|
|
285
|
+
);
|
|
286
|
+
};
|
|
287
|
+
|
|
288
|
+
export const CheckoutPage = ({ cartId }: { cartId: string }) => (
|
|
289
|
+
<CheckoutProvider storefront={storefront} cartId={cartId}>
|
|
290
|
+
<MyCheckout />
|
|
291
|
+
</CheckoutProvider>
|
|
292
|
+
);
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
What each part does:
|
|
296
|
+
|
|
297
|
+
- `CheckoutLayout` renders the top bar and a container with the steps on the left and the summary on the right. The top bar holds the logo, the stepper and, on mobile, the summary drawer. With `hideTopBar` those disappear too.
|
|
298
|
+
- `CheckoutForm` renders the active step, the next and back buttons and error messages, and submits the step.
|
|
299
|
+
- `CheckoutSignIn`, `CheckoutForgotPassword` and `CheckoutAccountSelection` are the screens around signing in. `<Checkout />` shows them based on `showSignIn`, `showForgotPassword` and `showAccountSelection` from `useCheckout()`. If you compose your own checkout, render them yourself.
|
|
300
|
+
- `CheckoutSummary`, `CheckoutStepper` and `CheckoutTopBar` can also be placed on their own.
|
|
301
|
+
- Below the steps sit the sections (`CheckoutShippingAddressSection`, `CheckoutPaymentMethodSection`, ...) and fields (`CheckoutShippingMethodsField`, `CheckoutPaymentMethodsField`, ...). Use these to build your own step components.
|
|
302
|
+
|
|
303
|
+
`useCheckout()` gives access to the full state, including `flow`, `summary`, `activeStep`, `steps`, `settings`, `translations`, `isLoading`, `notFound`, the actions (`addCoupon`, `addShippingMethod`, `checkout`, ...) and `track`.
|
|
304
|
+
|
|
305
|
+
The package also exports its GraphQL `queries`, `mutations` and `fragments`, the `hooks`, `utils`, `constants` and the payment method, issuer and carrier `icons`. Besides the root import you can import a single folder, for example `@afosto/checkout-ui/steps`.
|
|
306
|
+
|
|
307
|
+
## Development
|
|
308
|
+
|
|
309
|
+
```bash
|
|
310
|
+
npx nx build @afosto/checkout-ui
|
|
311
|
+
npx nx lint @afosto/checkout-ui
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
The build turns every `.ts` file under `src` into its own entry point. Add new components to `src/index.ts` to export them from the root.
|
|
315
|
+
|
|
316
|
+
### Project layout
|
|
317
|
+
|
|
318
|
+
```
|
|
319
|
+
src/
|
|
320
|
+
Checkout*/ one folder per component, each with an index.ts
|
|
321
|
+
CheckoutProvider/ provider, theme provider and query client provider
|
|
322
|
+
steps/ default step definitions
|
|
323
|
+
operations/ default API operations for cart, order and auth
|
|
324
|
+
queries/ GraphQL queries
|
|
325
|
+
mutations/ GraphQL mutations
|
|
326
|
+
fragments/ GraphQL fragments
|
|
327
|
+
hooks/ state hooks behind the providers
|
|
328
|
+
constants/ analytics events, icon mappings, address field config
|
|
329
|
+
icons/ payment method, issuer and carrier icons
|
|
330
|
+
utils/ formatting and helper functions
|
|
331
|
+
types.ts shared types
|
|
332
|
+
index.ts public exports
|
|
333
|
+
```
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CheckoutAddressFields.d.ts","sourceRoot":"","sources":["../../src/CheckoutAddressFields/CheckoutAddressFields.tsx"],"names":[],"mappings":"AAEA,OAAO,EAGL,KAAK,cAAc,EAIpB,MAAM,OAAO,CAAC;AACf,OAAO,EAQL,KAAK,aAAa,EACnB,MAAM,oBAAoB,CAAC;AAE5B,OAAO,EAEL,KAAK,wBAAwB,EAC9B,MAAM,4CAA4C,CAAC;AAYpD,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,UAAU,CAAC;AAEzD,MAAM,WAAW,0BAA0B;IACzC,gBAAgB,CAAC,EAAE,wBAAwB,CAAC;IAC5C,2BAA2B,CAAC,EAAE,OAAO,CAAC;IACtC,qBAAqB,EAAE,MAAM,EAAE,CAAC;IAChC,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACrC,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,eAAe,CAAC,EAAE,CAChB,KAAK,EAAE,cAAc,CAAC,OAAO,EAAE,KAAK,CAAC,EACrC,OAAO,EAAE,aAAa,GAAG,IAAI,KAC1B,IAAI,CAAC;IACV,wBAAwB,CAAC,EAAE,wBAAwB,CAAC;IACpD,qBAAqB,CAAC,EAAE,OAAO,CAAC;IAChC,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B,KAAK,EAAE,MAAM,CAAC;IACd,gBAAgB,EAAE,MAAM,CAAC;CAC1B;AAED,eAAO,MAAM,qBAAqB,GAAI,OAAO,0BAA0B,
|
|
1
|
+
{"version":3,"file":"CheckoutAddressFields.d.ts","sourceRoot":"","sources":["../../src/CheckoutAddressFields/CheckoutAddressFields.tsx"],"names":[],"mappings":"AAEA,OAAO,EAGL,KAAK,cAAc,EAIpB,MAAM,OAAO,CAAC;AACf,OAAO,EAQL,KAAK,aAAa,EACnB,MAAM,oBAAoB,CAAC;AAE5B,OAAO,EAEL,KAAK,wBAAwB,EAC9B,MAAM,4CAA4C,CAAC;AAYpD,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,UAAU,CAAC;AAEzD,MAAM,WAAW,0BAA0B;IACzC,gBAAgB,CAAC,EAAE,wBAAwB,CAAC;IAC5C,2BAA2B,CAAC,EAAE,OAAO,CAAC;IACtC,qBAAqB,EAAE,MAAM,EAAE,CAAC;IAChC,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACrC,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,eAAe,CAAC,EAAE,CAChB,KAAK,EAAE,cAAc,CAAC,OAAO,EAAE,KAAK,CAAC,EACrC,OAAO,EAAE,aAAa,GAAG,IAAI,KAC1B,IAAI,CAAC;IACV,wBAAwB,CAAC,EAAE,wBAAwB,CAAC;IACpD,qBAAqB,CAAC,EAAE,OAAO,CAAC;IAChC,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B,KAAK,EAAE,MAAM,CAAC;IACd,gBAAgB,EAAE,MAAM,CAAC;CAC1B;AAED,eAAO,MAAM,qBAAqB,GAAI,OAAO,0BAA0B,qDA+etE,CAAC"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { jsxs as p, Fragment as M, jsx as i } from "react/jsx-runtime";
|
|
2
|
-
import { useRef as
|
|
3
|
-
import { useFormContext as Ae, Fields as
|
|
2
|
+
import { useRef as g, useEffect as k, useCallback as Ce } from "react";
|
|
3
|
+
import { useFormContext as Ae, Fields as n, Grid as d, Typography as Fe, Collapse as B, Box as ge } from "@afosto/components";
|
|
4
4
|
import { isDefined as R, capitalize as I, decamelize as G, camelize as j } from "@afosto/utils";
|
|
5
5
|
import { CheckoutAddressCard as Se } from "../CheckoutAddressCard/CheckoutAddressCard.js";
|
|
6
6
|
import { calculateBreakpointsForAddressFields as ye } from "../utils/calculateBreakpointsForAddressFields.js";
|
|
@@ -12,36 +12,36 @@ const ke = (U) => {
|
|
|
12
12
|
const {
|
|
13
13
|
addressCardProps: q = {},
|
|
14
14
|
alwaysShowAddressLineFields: T = !1,
|
|
15
|
-
availableCountryCodes:
|
|
15
|
+
availableCountryCodes: S,
|
|
16
16
|
fieldLabels: v = {},
|
|
17
17
|
isSalutationRequired: H = !1,
|
|
18
18
|
name: e,
|
|
19
|
-
onChangeCountry:
|
|
19
|
+
onChangeCountry: y,
|
|
20
20
|
onChangeValidationSchema: Y,
|
|
21
21
|
showOrganisationField: J,
|
|
22
22
|
showSalutationField: L,
|
|
23
23
|
title: Q,
|
|
24
24
|
validationSchema: X
|
|
25
|
-
} = U, D =
|
|
25
|
+
} = U, D = g(null), E = g(null), z = g(null), _ = g(null), { getValues: x, reset: Z, setValue: c, watch: f } = Ae(), b = f(e, {}), u = f(`${e}.country`), K = u?.alpha2Code, ee = f(`${e}.postalCode`), te = f(`${e}.premiseNumber`), {
|
|
26
26
|
autocompletedAddress: oe,
|
|
27
27
|
handleAutocompleteAddress: w,
|
|
28
28
|
handleBlurAutocompleteAddressField: h,
|
|
29
29
|
handleShowAddressLines: ie,
|
|
30
30
|
showAddressLineFields: W
|
|
31
31
|
} = be({ alwaysShowAddressLineFields: T, name: e }), {
|
|
32
|
-
administrativeAreas:
|
|
32
|
+
administrativeAreas: $,
|
|
33
33
|
areFieldsAllowed: C,
|
|
34
34
|
fieldFormats: A,
|
|
35
35
|
fieldTypes: ae,
|
|
36
36
|
isFetchingAddressOptions: O
|
|
37
37
|
} = xe({
|
|
38
38
|
enabled: !0,
|
|
39
|
-
countryCode:
|
|
39
|
+
countryCode: u?.alpha2Code,
|
|
40
40
|
isSalutationRequired: H,
|
|
41
41
|
onChangeValidationSchema: Y,
|
|
42
42
|
salutation: L,
|
|
43
43
|
validationSchema: X
|
|
44
|
-
}),
|
|
44
|
+
}), N = R(K) && R(ee) && R(te);
|
|
45
45
|
$e({ fieldFormats: A, name: e }), k(() => {
|
|
46
46
|
const t = document.activeElement;
|
|
47
47
|
[
|
|
@@ -49,35 +49,37 @@ const ke = (U) => {
|
|
|
49
49
|
E.current,
|
|
50
50
|
z.current,
|
|
51
51
|
_.current
|
|
52
|
-
].every((
|
|
52
|
+
].every((r) => r !== t) && N && w(!0).catch(() => {
|
|
53
53
|
});
|
|
54
|
-
}, [w,
|
|
55
|
-
const t =
|
|
54
|
+
}, [w, N]), k(() => {
|
|
55
|
+
const t = x(`${e}.administrativeArea`);
|
|
56
56
|
if (t && typeof t == "string") {
|
|
57
|
-
const o =
|
|
58
|
-
(
|
|
57
|
+
const o = $.find(
|
|
58
|
+
(r) => r.code === t
|
|
59
59
|
);
|
|
60
60
|
o && c(`${e}.administrativeArea`, o);
|
|
61
61
|
} else !t && typeof t == "string" && c(`${e}.administrativeArea`, null);
|
|
62
|
-
}, [
|
|
63
|
-
const
|
|
64
|
-
(t) =>
|
|
65
|
-
[
|
|
62
|
+
}, [$, e, x, c]);
|
|
63
|
+
const ne = W && C(["thoroughfare", "locality"]) && !O, re = Ce(
|
|
64
|
+
(t) => S.includes("*") || S.includes(t?.alpha2Code),
|
|
65
|
+
[S]
|
|
66
66
|
), a = (t) => {
|
|
67
67
|
const o = ae[t];
|
|
68
68
|
if (!o)
|
|
69
69
|
return v?.[t] || I(G(t)?.replace(/_/g, " "));
|
|
70
|
-
const
|
|
70
|
+
const r = j(
|
|
71
71
|
o
|
|
72
|
-
),
|
|
73
|
-
return v?.[
|
|
72
|
+
), F = `${Ne[r] || r}`;
|
|
73
|
+
return v?.[F] || I(G(o));
|
|
74
74
|
}, se = (t, o) => {
|
|
75
|
-
const
|
|
76
|
-
|
|
77
|
-
|
|
75
|
+
const r = x();
|
|
76
|
+
y && typeof y == "function" && y(t, o);
|
|
77
|
+
const { avatar: F, ...m } = o || {};
|
|
78
|
+
Z({
|
|
79
|
+
...r,
|
|
78
80
|
[e]: {
|
|
79
|
-
...
|
|
80
|
-
country: o,
|
|
81
|
+
...r[e] || {},
|
|
82
|
+
country: o ? m : null,
|
|
81
83
|
postalCode: "",
|
|
82
84
|
premiseNumber: "",
|
|
83
85
|
premiseNumberSuffix: "",
|
|
@@ -98,7 +100,7 @@ const ke = (U) => {
|
|
|
98
100
|
xs: 12,
|
|
99
101
|
md: 12
|
|
100
102
|
},
|
|
101
|
-
component:
|
|
103
|
+
component: n.TextField,
|
|
102
104
|
props: {
|
|
103
105
|
label: a("organisation"),
|
|
104
106
|
name: `${e}.organisation`,
|
|
@@ -113,7 +115,7 @@ const ke = (U) => {
|
|
|
113
115
|
xs: 12,
|
|
114
116
|
md: 4
|
|
115
117
|
},
|
|
116
|
-
component:
|
|
118
|
+
component: n.TextField,
|
|
117
119
|
props: {
|
|
118
120
|
label: a("givenName"),
|
|
119
121
|
name: `${e}.givenName`,
|
|
@@ -128,7 +130,7 @@ const ke = (U) => {
|
|
|
128
130
|
md: 4
|
|
129
131
|
},
|
|
130
132
|
grow: !1,
|
|
131
|
-
component:
|
|
133
|
+
component: n.TextField,
|
|
132
134
|
props: {
|
|
133
135
|
label: a("additionalName"),
|
|
134
136
|
name: `${e}.additionalName`,
|
|
@@ -142,7 +144,7 @@ const ke = (U) => {
|
|
|
142
144
|
xs: 12,
|
|
143
145
|
md: 4
|
|
144
146
|
},
|
|
145
|
-
component:
|
|
147
|
+
component: n.TextField,
|
|
146
148
|
props: {
|
|
147
149
|
label: a("familyName"),
|
|
148
150
|
name: `${e}.familyName`,
|
|
@@ -156,7 +158,7 @@ const ke = (U) => {
|
|
|
156
158
|
xs: 12,
|
|
157
159
|
md: 6
|
|
158
160
|
},
|
|
159
|
-
component:
|
|
161
|
+
component: n.TextField,
|
|
160
162
|
props: {
|
|
161
163
|
label: a("addressLine2"),
|
|
162
164
|
name: `${e}.addressLine2`,
|
|
@@ -170,7 +172,7 @@ const ke = (U) => {
|
|
|
170
172
|
xs: 12,
|
|
171
173
|
md: 4
|
|
172
174
|
},
|
|
173
|
-
component:
|
|
175
|
+
component: n.TextField,
|
|
174
176
|
props: {
|
|
175
177
|
label: a("postalCode"),
|
|
176
178
|
name: `${e}.postalCode`,
|
|
@@ -188,7 +190,7 @@ const ke = (U) => {
|
|
|
188
190
|
md: 3
|
|
189
191
|
},
|
|
190
192
|
grow: !1,
|
|
191
|
-
component:
|
|
193
|
+
component: n.TextField,
|
|
192
194
|
props: {
|
|
193
195
|
label: a("premiseNumber"),
|
|
194
196
|
name: `${e}.premiseNumber`,
|
|
@@ -204,7 +206,7 @@ const ke = (U) => {
|
|
|
204
206
|
md: 3
|
|
205
207
|
},
|
|
206
208
|
grow: !1,
|
|
207
|
-
component:
|
|
209
|
+
component: n.TextField,
|
|
208
210
|
props: {
|
|
209
211
|
label: a("premiseNumberSuffix"),
|
|
210
212
|
name: `${e}.premiseNumberSuffix`,
|
|
@@ -219,7 +221,7 @@ const ke = (U) => {
|
|
|
219
221
|
xs: 12,
|
|
220
222
|
md: 6
|
|
221
223
|
},
|
|
222
|
-
component:
|
|
224
|
+
component: n.TextField,
|
|
223
225
|
props: {
|
|
224
226
|
label: a("thoroughfare"),
|
|
225
227
|
name: `${e}.thoroughfare`,
|
|
@@ -233,7 +235,7 @@ const ke = (U) => {
|
|
|
233
235
|
xs: 12,
|
|
234
236
|
md: 6
|
|
235
237
|
},
|
|
236
|
-
component:
|
|
238
|
+
component: n.TextField,
|
|
237
239
|
props: {
|
|
238
240
|
label: a("locality"),
|
|
239
241
|
name: `${e}.locality`,
|
|
@@ -247,29 +249,29 @@ const ke = (U) => {
|
|
|
247
249
|
xs: 12,
|
|
248
250
|
md: 6
|
|
249
251
|
},
|
|
250
|
-
component:
|
|
252
|
+
component: n.Autocomplete,
|
|
251
253
|
props: {
|
|
252
254
|
label: a("administrativeArea"),
|
|
253
255
|
name: `${e}.administrativeArea`,
|
|
254
256
|
id: `${e}AdministrativeArea`,
|
|
255
|
-
options:
|
|
257
|
+
options: $,
|
|
256
258
|
fullWidth: !0
|
|
257
259
|
}
|
|
258
260
|
}
|
|
259
261
|
}, de = l.locality.component, me = l.thoroughfare.component, ue = () => {
|
|
260
262
|
const t = ["xs", "md"];
|
|
261
263
|
let o = A.fields || Re;
|
|
262
|
-
return
|
|
263
|
-
const
|
|
264
|
+
return u?.alpha2Code === "NL" && (o = Te), u?.alpha2Code === "SG" && (o = ve), u?.alpha2Code === "MO" && (o = Le), o.reduce((r, F) => {
|
|
265
|
+
const m = F.map((s) => j(s)).filter(
|
|
264
266
|
(s) => !!l[s] && C([s])
|
|
265
267
|
);
|
|
266
|
-
if (!
|
|
267
|
-
return
|
|
268
|
+
if (!m.length)
|
|
269
|
+
return r;
|
|
268
270
|
const pe = ye(
|
|
269
|
-
|
|
271
|
+
m,
|
|
270
272
|
t,
|
|
271
273
|
l
|
|
272
|
-
), V =
|
|
274
|
+
), V = m.map((s) => {
|
|
273
275
|
const ce = l[s].component, fe = t.reduce(
|
|
274
276
|
(he, P) => ({
|
|
275
277
|
...he,
|
|
@@ -279,16 +281,16 @@ const ke = (U) => {
|
|
|
279
281
|
);
|
|
280
282
|
return /* @__PURE__ */ i(d, { item: !0, ...fe, children: /* @__PURE__ */ i(ce, { ...l[s].props }) }, s);
|
|
281
283
|
});
|
|
282
|
-
return
|
|
283
|
-
...
|
|
284
|
-
/* @__PURE__ */ i(d, { item: !0, container: !0, spacing: 2, children: V },
|
|
285
|
-
] : [...
|
|
284
|
+
return m.length > 1 ? [
|
|
285
|
+
...r,
|
|
286
|
+
/* @__PURE__ */ i(d, { item: !0, container: !0, spacing: 2, children: V }, m.join("."))
|
|
287
|
+
] : [...r, ...V];
|
|
286
288
|
}, []);
|
|
287
289
|
};
|
|
288
290
|
return /* @__PURE__ */ p(M, { children: [
|
|
289
291
|
/* @__PURE__ */ p(d, { container: !0, spacing: 2, children: [
|
|
290
292
|
/* @__PURE__ */ i(d, { item: !0, xs: 12, children: /* @__PURE__ */ i(
|
|
291
|
-
|
|
293
|
+
n.CountrySelect,
|
|
292
294
|
{
|
|
293
295
|
label: a("country"),
|
|
294
296
|
name: `${e}.country`,
|
|
@@ -296,7 +298,7 @@ const ke = (U) => {
|
|
|
296
298
|
inputRef: D,
|
|
297
299
|
onBlur: h,
|
|
298
300
|
onChange: se,
|
|
299
|
-
filterCountryOptions:
|
|
301
|
+
filterCountryOptions: re,
|
|
300
302
|
sortBy: "name",
|
|
301
303
|
fullWidth: !0
|
|
302
304
|
}
|
|
@@ -304,15 +306,15 @@ const ke = (U) => {
|
|
|
304
306
|
L && /* @__PURE__ */ p(d, { item: !0, xs: 12, children: [
|
|
305
307
|
/* @__PURE__ */ i(Fe, { sx: { fontWeight: 500, mb: 0.5 }, children: a("salutation") }),
|
|
306
308
|
/* @__PURE__ */ p(
|
|
307
|
-
|
|
309
|
+
n.RadioGroup,
|
|
308
310
|
{
|
|
309
311
|
id: `${e}Salutation`,
|
|
310
312
|
name: `${e}.salutation`,
|
|
311
313
|
row: !0,
|
|
312
314
|
children: [
|
|
313
|
-
/* @__PURE__ */ i(
|
|
315
|
+
/* @__PURE__ */ i(n.Radio, { label: a("salutationMr"), value: "mr" }),
|
|
314
316
|
/* @__PURE__ */ i(
|
|
315
|
-
|
|
317
|
+
n.Radio,
|
|
316
318
|
{
|
|
317
319
|
label: a("salutationMrs"),
|
|
318
320
|
value: "mrs"
|
|
@@ -324,24 +326,24 @@ const ke = (U) => {
|
|
|
324
326
|
] }),
|
|
325
327
|
!O && /* @__PURE__ */ i(M, { children: ue() })
|
|
326
328
|
] }),
|
|
327
|
-
|
|
329
|
+
u?.alpha2Code === "NL" && /* @__PURE__ */ i(B, { in: ne, children: /* @__PURE__ */ p(d, { container: !0, spacing: 2, sx: { pt: 2 }, children: [
|
|
328
330
|
C(["thoroughfare"]) && /* @__PURE__ */ i(d, { item: !0, xs: 12, children: /* @__PURE__ */ i(me, { ...l.thoroughfare.props }) }),
|
|
329
331
|
C(["locality"]) && /* @__PURE__ */ i(d, { item: !0, xs: 12, children: /* @__PURE__ */ i(de, { ...l.locality.props }) })
|
|
330
332
|
] }) }),
|
|
331
333
|
/* @__PURE__ */ i(
|
|
332
334
|
B,
|
|
333
335
|
{
|
|
334
|
-
in: !W &&
|
|
336
|
+
in: !W && N && !!oe,
|
|
335
337
|
sx: { width: "100%" },
|
|
336
338
|
children: /* @__PURE__ */ i(ge, { mt: 4, children: /* @__PURE__ */ i(
|
|
337
339
|
Se,
|
|
338
340
|
{
|
|
339
341
|
...q,
|
|
340
342
|
address: {
|
|
341
|
-
...
|
|
342
|
-
countryCode:
|
|
343
|
+
...b,
|
|
344
|
+
countryCode: b?.country?.alpha2Code,
|
|
343
345
|
options: {
|
|
344
|
-
...
|
|
346
|
+
...b?.options || {},
|
|
345
347
|
format: A.address
|
|
346
348
|
}
|
|
347
349
|
},
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { CheckoutAgreementOptionConditionType, DeepPartial } from '../types';
|
|
2
2
|
declare const defaultTranslations: {
|
|
3
3
|
payImmediately: string;
|
|
4
|
-
|
|
4
|
+
remainingAmountDueBeforeDelivery: string;
|
|
5
5
|
withinDaysOfTheInvoiceDate: string;
|
|
6
6
|
};
|
|
7
7
|
export interface CheckoutAgreementOptionConditionProps {
|