-
Notifications
You must be signed in to change notification settings - Fork 334
feat(clerk-js,types,clerk-react): Add internal open/close checkout methods #5481
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
dfe84ea
44d8879
c22a321
8acac44
bb4212b
41fd1a0
e9e59d7
5c28068
0151de0
2b31f6e
1e2e028
f779ac7
1608130
d29bf85
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
--- | ||
'@clerk/clerk-js': patch | ||
'@clerk/clerk-react': patch | ||
'@clerk/types': patch | ||
--- | ||
|
||
Introduce `clerk.__internal_openCheckout()` and `clerk.__internal_closeCheckout()` methods and remove `<Checkout />` from within the `<PricingTable />` component. |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,43 +1,25 @@ | ||
import type { __experimental_CheckoutProps } from '@clerk/types'; | ||
|
||
import { PROFILE_CARD_SCROLLBOX_ID } from '../../constants'; | ||
import { useCheckoutContext, withCoreUserGuard } from '../../contexts'; | ||
import { __experimental_CheckoutContext } from '../../contexts'; | ||
import { Flow } from '../../customizables'; | ||
import { Drawer } from '../../elements'; | ||
import { Route, Switch } from '../../router'; | ||
import { CheckoutPage } from './CheckoutPage'; | ||
|
||
export const __experimental_Checkout = (props: __experimental_CheckoutProps) => { | ||
return ( | ||
<Flow.Root flow='checkout'> | ||
<Flow.Part> | ||
<Switch> | ||
<Route> | ||
<AuthenticatedRoutes {...props} /> | ||
</Route> | ||
</Switch> | ||
<__experimental_CheckoutContext.Provider | ||
value={{ | ||
componentName: 'Checkout', | ||
}} | ||
> | ||
<Drawer.Content> | ||
<Drawer.Header title='Checkout' /> | ||
<CheckoutPage {...props} /> | ||
</Drawer.Content> | ||
</__experimental_CheckoutContext.Provider> | ||
</Flow.Part> | ||
</Flow.Root> | ||
); | ||
}; | ||
|
||
const AuthenticatedRoutes = withCoreUserGuard((props: __experimental_CheckoutProps) => { | ||
const { mode = 'mounted', isOpen = false, setIsOpen = () => {} } = useCheckoutContext(); | ||
|
||
return ( | ||
<Drawer.Root | ||
open={isOpen} | ||
onOpenChange={setIsOpen} | ||
strategy={mode === 'mounted' ? 'fixed' : 'absolute'} | ||
portalProps={{ | ||
id: mode === 'modal' ? PROFILE_CARD_SCROLLBOX_ID : undefined, | ||
}} | ||
> | ||
<Drawer.Overlay /> | ||
<Drawer.Content> | ||
<Drawer.Header title='Checkout' /> | ||
<CheckoutPage {...props} /> | ||
</Drawer.Content> | ||
</Drawer.Root> | ||
); | ||
}); |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -8,10 +8,9 @@ import type { | |
import { useState } from 'react'; | ||
|
||
import { PROFILE_CARD_SCROLLBOX_ID } from '../../constants'; | ||
import { __experimental_CheckoutContext, usePricingTableContext } from '../../contexts'; | ||
import { usePricingTableContext } from '../../contexts'; | ||
import { AppearanceProvider } from '../../customizables'; | ||
import { usePlans } from '../../hooks'; | ||
import { __experimental_Checkout } from '../Checkout'; | ||
import { PricingTableDefault } from './PricingTableDefault'; | ||
import { PricingTableMatrix } from './PricingTableMatrix'; | ||
import { SubscriptionDetailDrawer } from './SubscriptionDetailDrawer'; | ||
|
@@ -25,10 +24,8 @@ export const __experimental_PricingTable = (props: __experimental_PricingTablePr | |
const { plans, subscriptions, revalidate } = usePlans({ subscriberType }); | ||
|
||
const [planPeriod, setPlanPeriod] = useState<__experimental_CommerceSubscriptionPlanPeriod>('month'); | ||
const [checkoutPlan, setCheckoutPlan] = useState<__experimental_CommercePlanResource>(); | ||
const [detailSubscription, setDetailSubscription] = useState<__experimental_CommerceSubscriptionResource>(); | ||
|
||
const [showCheckout, setShowCheckout] = useState(false); | ||
const [showSubscriptionDetailDrawer, setShowSubscriptionDetailDrawer] = useState(false); | ||
|
||
const selectPlan = (plan: __experimental_CommercePlanResource) => { | ||
|
@@ -40,8 +37,13 @@ export const __experimental_PricingTable = (props: __experimental_PricingTablePr | |
setDetailSubscription(activeSubscription); | ||
setShowSubscriptionDetailDrawer(true); | ||
} else { | ||
setCheckoutPlan(plan); | ||
setShowCheckout(true); | ||
clerk.__internal_openCheckout({ | ||
planId: plan.id, | ||
planPeriod, | ||
orgId: subscriberType === 'org' ? organization?.id : undefined, | ||
onSubscriptionComplete: onSubscriptionChange, | ||
portalId: mode === 'modal' ? PROFILE_CARD_SCROLLBOX_ID : undefined, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. does As a semi-related nit, I might rename There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Mode is set on the PricingTable and I think was originally adding following the other components, but there is no modal mode for PricingTable, so yeah, it is confusing, as its only a mounted component. Will refactor naming approach in a follow up PR.
Comment on lines
+40
to
+45
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is much cleaner! |
||
}); | ||
} | ||
}; | ||
|
||
|
@@ -74,26 +76,6 @@ export const __experimental_PricingTable = (props: __experimental_PricingTablePr | |
appearanceKey='checkout' | ||
appearance={props.checkoutProps?.appearance} | ||
> | ||
<__experimental_CheckoutContext.Provider | ||
value={{ | ||
componentName: 'Checkout', | ||
mode, | ||
isOpen: showCheckout, | ||
setIsOpen: setShowCheckout, | ||
}} | ||
> | ||
{/*TODO: Used by InvisibleRootBox, can we simplify? */} | ||
<div> | ||
{checkoutPlan && ( | ||
<__experimental_Checkout | ||
planPeriod={planPeriod} | ||
planId={checkoutPlan.id} | ||
orgId={subscriberType === 'org' ? organization?.id : undefined} | ||
onSubscriptionComplete={onSubscriptionChange} | ||
/> | ||
)} | ||
</div> | ||
</__experimental_CheckoutContext.Provider> | ||
<SubscriptionDetailDrawer | ||
isOpen={showSubscriptionDetailDrawer} | ||
setIsOpen={setShowSubscriptionDetailDrawer} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Introducing new drawer specific methods as we want to have an exit animation for the drawers, and removing all the props causes issues with portaling into the profile components.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It would be great to consolidate the modal/drawer handling at some point in the future. I would expect them to function the same, just be a different flavor of UI. Maybe there's a good reason to keep them separate though? 🤔
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we want modals to have an exit animation, we'd need to follow this updated drawer pattern, currently all modals are removed immediately but wiping out the props.