@graphcommerce/magento-cart-payment-method 3.0.7 → 3.1.0

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.
@@ -19,6 +19,8 @@ export type PaymentButtonProps = PaymentMethod & { buttonProps: LinkOrButtonProp
19
19
  export type PaymentOptionsProps = PaymentMethod & PaymentMethodOptionsProps
20
20
 
21
21
  export type PaymentToggleProps = PaymentMethod
22
+ export type PaymentHandlerProps = { code: string }
23
+
22
24
  export type ExpandPaymentMethods = (
23
25
  available: AvailablePaymentMethodFragment,
24
26
  context: PaymentMethodContextFragment,
@@ -32,7 +34,7 @@ export interface PaymentModule {
32
34
  PaymentButton?: React.VFC<PaymentButtonProps>
33
35
  PaymentToggle?: React.VFC<PaymentToggleProps>
34
36
  expandMethods?: ExpandPaymentMethods
35
- PaymentHandler?: React.VFC
37
+ PaymentHandler?: React.VFC<PaymentHandlerProps>
36
38
  }
37
39
 
38
40
  export type PaymentMethodModules = { [code: string]: PaymentModule }
package/CHANGELOG.md CHANGED
@@ -1,5 +1,23 @@
1
1
  # Change Log
2
2
 
3
+ ## 3.1.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [#1379](https://github.com/graphcommerce-org/graphcommerce/pull/1379) [`6ebe9d12d`](https://github.com/graphcommerce-org/graphcommerce/commit/6ebe9d12db9fcaa2af67a475e64a08d63e232b46) Thanks [@paales](https://github.com/paales)! - Added a `code` prop to the PaymentHandler so the handler can be reused by multiple methods without interfering.
8
+
9
+ ### Patch Changes
10
+
11
+ - [#1379](https://github.com/graphcommerce-org/graphcommerce/pull/1379) [`b8d04130a`](https://github.com/graphcommerce-org/graphcommerce/commit/b8d04130a1b1cb8fc85308939235140288744465) Thanks [@paales](https://github.com/paales)! - Removed unused trigger: onChange for useForm handler
12
+
13
+ - Updated dependencies [[`3c801f45c`](https://github.com/graphcommerce-org/graphcommerce/commit/3c801f45c7df55131acf30ae2fe0d2344830d480), [`b8d04130a`](https://github.com/graphcommerce-org/graphcommerce/commit/b8d04130a1b1cb8fc85308939235140288744465), [`3192fab82`](https://github.com/graphcommerce-org/graphcommerce/commit/3192fab82560e2211dfcacadc3b0b305260527d8), [`104abd14e`](https://github.com/graphcommerce-org/graphcommerce/commit/104abd14e1585ef0d8de77937d25156b8fa1e201), [`0e425e85e`](https://github.com/graphcommerce-org/graphcommerce/commit/0e425e85ee8fed280349317ee0440c7bceea5823), [`2a125b1f9`](https://github.com/graphcommerce-org/graphcommerce/commit/2a125b1f98bb9272d96c3577f21d6c984caad892), [`8a354d1cd`](https://github.com/graphcommerce-org/graphcommerce/commit/8a354d1cd4757497ddfc9b1969a0addbc8ff616b)]:
14
+ - @graphcommerce/next-ui@4.6.0
15
+ - @graphcommerce/magento-cart@4.2.4
16
+ - @graphcommerce/react-hook-form@3.1.0
17
+ - @graphcommerce/image@3.1.4
18
+ - @graphcommerce/framer-scroller@2.1.5
19
+ - @graphcommerce/magento-store@4.1.6
20
+
3
21
  ## 3.0.7
4
22
 
5
23
  ### Patch Changes
@@ -20,7 +20,7 @@ const paymentMethodContext = React.createContext<PaymentMethodContextProps>({
20
20
  })
21
21
  paymentMethodContext.displayName = 'PaymentMethodContext'
22
22
 
23
- type PaymentMethodContextProviderProps = PropsWithChildren<{ modules: PaymentMethodModules }>
23
+ export type PaymentMethodContextProviderProps = PropsWithChildren<{ modules: PaymentMethodModules }>
24
24
 
25
25
  export function PaymentMethodContextProvider(props: PaymentMethodContextProviderProps) {
26
26
  const { modules, children } = props
@@ -32,36 +32,28 @@ export function PaymentMethodContextProvider(props: PaymentMethodContextProvider
32
32
  const [selectedMethod, setSelectedMethod] = useState<PaymentMethod>()
33
33
  const [selectedModule, setSelectedModule] = useState<PaymentModule>()
34
34
 
35
+ const availableMethods = useMemo(() => {
36
+ const allMethods = cartContext?.available_payment_methods ?? []
37
+ const free = allMethods.find((method) => method?.code === 'free')
38
+
39
+ return free ? [free] : allMethods
40
+ }, [cartContext?.available_payment_methods])
41
+
35
42
  // Expand the payment methods
36
43
  useEffect(() => {
37
- // eslint-disable-next-line @typescript-eslint/no-floating-promises
44
+ if (!cartContext) return // eslint-disable-next-line @typescript-eslint/no-floating-promises
38
45
  ;(async () => {
39
- if (!cartContext) return
40
-
41
- const freeMethod = cartContext.available_payment_methods?.find(
42
- (method) => method?.code === 'free',
46
+ const promises = availableMethods.map(async (method) =>
47
+ method
48
+ ? modules[method.code]?.expandMethods?.(method, cartContext) ?? [{ ...method, child: '' }]
49
+ : Promise.resolve([]),
43
50
  )
44
51
 
45
- const promises =
46
- [...(freeMethod ? [freeMethod] : cartContext.available_payment_methods ?? [])].map(
47
- async (method) =>
48
- method
49
- ? modules?.[method.code]?.expandMethods?.(method, cartContext) ?? [
50
- { ...method, child: '' },
51
- ]
52
- : Promise.resolve([]),
53
- ) ?? []
54
-
55
52
  const loaded = (await Promise.all(promises)).flat(1).sort((a) => (a.preferred ? 1 : 0))
56
- const sortedMethods = loaded.sort((a, b) => {
57
- if (!modules?.[a?.code]) return 0
58
- if (!modules?.[b?.code]) return -1
59
- return 1
60
- })
61
53
 
62
- setMethods(sortedMethods)
54
+ setMethods(loaded)
63
55
  })()
64
- }, [cartContext, modules])
56
+ }, [availableMethods, cartContext, modules])
65
57
 
66
58
  const value = useMemo(
67
59
  () => ({
@@ -77,11 +69,9 @@ export function PaymentMethodContextProvider(props: PaymentMethodContextProvider
77
69
 
78
70
  return (
79
71
  <paymentMethodContext.Provider value={value}>
80
- {Object.entries(modules).map(([method, module]) => {
81
- const { PaymentHandler } = module
82
- if (!PaymentHandler) return null
83
- return <PaymentHandler key={method} />
84
- })}
72
+ {Object.entries(modules).map(([code, module]) =>
73
+ module.PaymentHandler ? <module.PaymentHandler key={code} code={code} /> : null,
74
+ )}
85
75
  {children}
86
76
  </paymentMethodContext.Provider>
87
77
  )
@@ -14,7 +14,7 @@ export function PaymentMethodPlaceOrderNoop(props: PaymentPlaceOrderProps) {
14
14
  const clearCurrentCartId = useClearCurrentCartId()
15
15
 
16
16
  const cartId = useCurrentCartId()
17
- const form = useFormGqlMutationCart(PaymentMethodPlaceOrderNoopDocument, { mode: 'onChange' })
17
+ const form = useFormGqlMutationCart(PaymentMethodPlaceOrderNoopDocument)
18
18
 
19
19
  const { handleSubmit, data, error } = form
20
20
  const { push } = useRouter()
@@ -16,10 +16,11 @@ import {
16
16
  UseFormComposeOptions,
17
17
  useFormPersist,
18
18
  } from '@graphcommerce/react-hook-form'
19
- import { t } from '@lingui/macro'
19
+ import { select, t } from '@lingui/macro'
20
20
  import { Box, FormControl, FormHelperText, SxProps, Theme } from '@mui/material'
21
21
  import { useEffect } from 'react'
22
22
  import { usePaymentMethodContext } from '../PaymentMethodContext/PaymentMethodContext'
23
+ import { useCartLock } from '../hooks/useCartLock'
23
24
 
24
25
  export type PaymentMethodTogglesProps = Pick<UseFormComposeOptions, 'step'> & {
25
26
  sx?: SxProps<Theme>
@@ -52,10 +53,11 @@ export function PaymentMethodToggles(props: PaymentMethodTogglesProps) {
52
53
  const { methods, selectedMethod, setSelectedMethod, setSelectedModule, modules } =
53
54
  usePaymentMethodContext()
54
55
 
55
- const form = useForm<{ code: string; paymentMethod?: string }>({
56
- mode: 'onChange',
57
- defaultValues: { code: selectedMethod?.code },
56
+ const [lockState] = useCartLock()
57
+ const form = useForm<{ code: string | null; paymentMethod?: string }>({
58
+ defaultValues: { code: lockState.method },
58
59
  })
60
+
59
61
  useFormPersist({ form, name: 'PaymentMethodToggle' })
60
62
 
61
63
  const { control, handleSubmit, watch, register, setValue, formState } = form
@@ -64,6 +66,10 @@ export function PaymentMethodToggles(props: PaymentMethodTogglesProps) {
64
66
 
65
67
  useFormCompose({ form, step, submit: submitHandler, key: 'PaymentMethodToggles' })
66
68
 
69
+ useEffect(() => {
70
+ if (selectedMethod?.code) setValue('code', selectedMethod.code)
71
+ }, [selectedMethod?.code, setValue])
72
+
67
73
  const paymentMethod = watch('paymentMethod')
68
74
  useEffect(() => {
69
75
  const [code, child] = paymentMethod?.split('___') ?? ['']
@@ -142,6 +148,9 @@ export function PaymentMethodToggles(props: PaymentMethodTogglesProps) {
142
148
  >
143
149
  {methods?.map((pm) => {
144
150
  const buttonValue = pm.child ? `${pm.code}___${pm.child}` : pm.code
151
+
152
+ if (process.env.NODE_ENV === 'production' && !modules?.[pm.code]) return null
153
+
145
154
  return (
146
155
  <ToggleButton
147
156
  name={name}
@@ -1,9 +1,11 @@
1
1
  import { useCurrentCartId } from '@graphcommerce/magento-cart'
2
2
  import { useUrlQuery } from '@graphcommerce/next-ui'
3
+ import { useState } from 'react'
3
4
 
4
5
  export type CartLockState = {
5
- cart_id?: string
6
- locked?: string
6
+ cart_id: string | null
7
+ locked: string | null
8
+ method: string | null
7
9
  }
8
10
 
9
11
  /**
@@ -12,15 +14,31 @@ export type CartLockState = {
12
14
  *
13
15
  * Todo: Block all operations on the cart while the cart is being blocked.
14
16
  */
15
- export function useCartLock<E extends Record<string, string | undefined>>() {
17
+ export function useCartLock<E extends CartLockState>() {
16
18
  const currentCartId = useCurrentCartId()
19
+ const [justLocked, setJustLocked] = useState(false)
20
+ const [queryState, setRouterQuery] = useUrlQuery<E>()
17
21
 
18
- const [queryState, setRouterQuery] = useUrlQuery<CartLockState & E>((params) => params)
19
-
20
- const lock = (params: CartLockState & E) => {
22
+ const lock = (params: Omit<E, 'locked' | 'cart_id'>) => {
21
23
  if (!currentCartId) return
22
- setRouterQuery({ locked: '1', cart_id: currentCartId, ...params })
24
+ setJustLocked(true)
25
+ setRouterQuery({
26
+ locked: '1',
27
+ cart_id: currentCartId,
28
+ ...params,
29
+ } as E)
30
+ }
31
+
32
+ const unlock = (params: Omit<E, 'locked' | 'cart_id' | 'method'>) => {
33
+ setRouterQuery({ cart_id: null, locked: null, method: null, ...params } as E)
34
+ return queryState
35
+ }
36
+
37
+ const resulting: Omit<E, 'locked'> & { locked: boolean; justLocked: boolean } = {
38
+ ...queryState,
39
+ locked: queryState.locked === '1',
40
+ justLocked,
23
41
  }
24
42
 
25
- return [queryState, lock] as const
43
+ return [resulting, lock, unlock] as const
26
44
  }
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@graphcommerce/magento-cart-payment-method",
3
3
  "homepage": "https://www.graphcommerce.org/",
4
4
  "repository": "github:graphcommerce-org/graphcommerce",
5
- "version": "3.0.7",
5
+ "version": "3.1.0",
6
6
  "sideEffects": false,
7
7
  "engines": {
8
8
  "node": "14.x"
@@ -22,13 +22,13 @@
22
22
  "type-fest": "2.12.1"
23
23
  },
24
24
  "dependencies": {
25
- "@graphcommerce/framer-scroller": "2.1.4",
25
+ "@graphcommerce/framer-scroller": "2.1.5",
26
26
  "@graphcommerce/graphql": "3.0.7",
27
- "@graphcommerce/image": "3.1.3",
28
- "@graphcommerce/magento-cart": "4.2.3",
29
- "@graphcommerce/magento-store": "4.1.5",
30
- "@graphcommerce/next-ui": "4.5.1",
31
- "@graphcommerce/react-hook-form": "3.0.7"
27
+ "@graphcommerce/image": "3.1.4",
28
+ "@graphcommerce/magento-cart": "4.2.4",
29
+ "@graphcommerce/magento-store": "4.1.6",
30
+ "@graphcommerce/next-ui": "4.6.0",
31
+ "@graphcommerce/react-hook-form": "3.1.0"
32
32
  },
33
33
  "peerDependencies": {
34
34
  "@lingui/macro": "^3.13.2",
@@ -7,9 +7,8 @@ export const goToPayment = async (
7
7
  page: Page,
8
8
  apolloClient: ApolloClient<NormalizedCacheObject>,
9
9
  ) => {
10
- await page.locator('a:has-text("View shopping cart")').click()
11
-
12
- await page.locator('text=Start Checkout').click()
10
+ await page.locator('#view-shopping-cart-button').click()
11
+ await page.locator('#cart-start-checkout').click()
13
12
 
14
13
  const email = page.locator('input[name="email"]')
15
14
  await email.click()
package/test/index.ts ADDED
@@ -0,0 +1,2 @@
1
+ export * from './goToPayment'
2
+ export * from './selectPaymentMethod'
@@ -0,0 +1,8 @@
1
+ import { Page } from '@playwright/test'
2
+
3
+ export const selectPaymentMethod = async (page: Page, method: string) => {
4
+ const locator = page.locator(`button[value=${method}]`)
5
+ await locator.click()
6
+
7
+ return locator
8
+ }