From 62c50dec2446135e5b3edbae06e02837d7a0f5ba Mon Sep 17 00:00:00 2001 From: panteliselef Date: Tue, 19 Aug 2025 18:54:17 +0300 Subject: [PATCH 1/8] feat(vue,astro): Add Billing buttons --- packages/astro/src/astro-components/index.ts | 3 + .../unstyled/CheckoutButton.astro | 76 +++++++++++++++++++ .../unstyled/PlanDetailsButton.astro | 63 +++++++++++++++ .../unstyled/SubscriptionDetailsButton.astro | 72 ++++++++++++++++++ packages/astro/src/react/CheckoutButton.tsx | 53 +++++++++++++ .../astro/src/react/PlanDetailsButton.tsx | 40 ++++++++++ .../src/react/SubscriptionDetailsButton.tsx | 45 +++++++++++ packages/astro/src/react/index.ts | 14 +++- packages/astro/src/react/utils.tsx | 11 ++- .../vue/src/components/CheckoutButton.vue | 53 +++++++++++++ .../vue/src/components/PlanDetailsButton.vue | 40 ++++++++++ .../components/SubscriptionDetailsButton.vue | 50 ++++++++++++ packages/vue/src/components/index.ts | 3 + packages/vue/src/index.ts | 6 ++ packages/vue/src/utils/childrenUtils.ts | 9 ++- 15 files changed, 534 insertions(+), 4 deletions(-) create mode 100644 packages/astro/src/astro-components/unstyled/CheckoutButton.astro create mode 100644 packages/astro/src/astro-components/unstyled/PlanDetailsButton.astro create mode 100644 packages/astro/src/astro-components/unstyled/SubscriptionDetailsButton.astro create mode 100644 packages/astro/src/react/CheckoutButton.tsx create mode 100644 packages/astro/src/react/PlanDetailsButton.tsx create mode 100644 packages/astro/src/react/SubscriptionDetailsButton.tsx create mode 100644 packages/vue/src/components/CheckoutButton.vue create mode 100644 packages/vue/src/components/PlanDetailsButton.vue create mode 100644 packages/vue/src/components/SubscriptionDetailsButton.vue diff --git a/packages/astro/src/astro-components/index.ts b/packages/astro/src/astro-components/index.ts index 180b6cac519..970c1f45cf9 100644 --- a/packages/astro/src/astro-components/index.ts +++ b/packages/astro/src/astro-components/index.ts @@ -12,6 +12,9 @@ export { default as AuthenticateWithRedirectCallback } from './control/Authentic export { default as SignInButton } from './unstyled/SignInButton.astro'; export { default as SignUpButton } from './unstyled/SignUpButton.astro'; export { default as SignOutButton } from './unstyled/SignOutButton.astro'; +export { default as SubscriptionDetailsButton } from './unstyled/SubscriptionDetailsButton.astro'; +export { default as CheckoutButton } from './unstyled/CheckoutButton.astro'; +export { default as PlanDetailsButton } from './unstyled/PlanDetailsButton.astro'; /** * UI Components diff --git a/packages/astro/src/astro-components/unstyled/CheckoutButton.astro b/packages/astro/src/astro-components/unstyled/CheckoutButton.astro new file mode 100644 index 00000000000..ac1eccba362 --- /dev/null +++ b/packages/astro/src/astro-components/unstyled/CheckoutButton.astro @@ -0,0 +1,76 @@ +--- +import type { HTMLTag, Polymorphic } from 'astro/types'; +import type { __experimental_CheckoutButtonProps } from '@clerk/types'; +import type { ButtonProps } from '../../types'; +import { addUnstyledAttributeToFirstTag, logAsPropUsageDeprecation } from './utils'; + +type Props = Polymorphic> & __experimental_CheckoutButtonProps; + +import { generateSafeId } from '@clerk/astro/internal'; + +const safeId = generateSafeId(); + +if ('as' in Astro.props) { + logAsPropUsageDeprecation(); +} + +const { + as: Tag = 'button', + asChild, + planId, + planPeriod, + for: _for, + onSubscriptionComplete, + newSubscriptionRedirectUrl, + checkoutProps, + ...props +} = Astro.props; + +const checkoutOptions = { + planId, + planPeriod, + for: _for, + onSubscriptionComplete, + newSubscriptionRedirectUrl, + ...checkoutProps, +}; + +let htmlElement = ''; + +if (asChild) { + htmlElement = await Astro.slots.render('default'); + htmlElement = addUnstyledAttributeToFirstTag(htmlElement, safeId); +} +--- + +{ + asChild ? ( + + ) : ( + + Checkout + + ) +} + + diff --git a/packages/astro/src/astro-components/unstyled/PlanDetailsButton.astro b/packages/astro/src/astro-components/unstyled/PlanDetailsButton.astro new file mode 100644 index 00000000000..6c102ef73a5 --- /dev/null +++ b/packages/astro/src/astro-components/unstyled/PlanDetailsButton.astro @@ -0,0 +1,63 @@ +--- +import type { HTMLTag, Polymorphic } from 'astro/types'; +import type { __experimental_PlanDetailsButtonProps } from '@clerk/types'; +import type { ButtonProps } from '../../types'; +import { addUnstyledAttributeToFirstTag, logAsPropUsageDeprecation } from './utils'; + +type Props = Polymorphic> & __experimental_PlanDetailsButtonProps; + +import { generateSafeId } from '@clerk/astro/internal'; + +const safeId = generateSafeId(); + +if ('as' in Astro.props) { + logAsPropUsageDeprecation(); +} + +const { + as: Tag = 'button', + asChild, + plan, + planId, + initialPlanPeriod, + planDetailsProps, + ...props +} = Astro.props; + +const planDetailsOptions = { + plan, + planId, + initialPlanPeriod, + ...planDetailsProps, +}; + +let htmlElement = ''; + +if (asChild) { + htmlElement = await Astro.slots.render('default'); + htmlElement = addUnstyledAttributeToFirstTag(htmlElement, safeId); +} +--- + +{ + asChild ? ( + + ) : ( + + Plan details + + ) +} + + diff --git a/packages/astro/src/astro-components/unstyled/SubscriptionDetailsButton.astro b/packages/astro/src/astro-components/unstyled/SubscriptionDetailsButton.astro new file mode 100644 index 00000000000..90b13578819 --- /dev/null +++ b/packages/astro/src/astro-components/unstyled/SubscriptionDetailsButton.astro @@ -0,0 +1,72 @@ +--- +import type { HTMLTag, Polymorphic } from 'astro/types'; +import type { __experimental_SubscriptionDetailsButtonProps } from '@clerk/types'; +import type { ButtonProps } from '../../types'; +import { addUnstyledAttributeToFirstTag, logAsPropUsageDeprecation } from './utils'; + +type Props = Polymorphic> & __experimental_SubscriptionDetailsButtonProps; + +import { generateSafeId } from '@clerk/astro/internal'; + +const safeId = generateSafeId(); + +if ('as' in Astro.props) { + logAsPropUsageDeprecation(); +} + +const { + as: Tag = 'button', + asChild, + for: _for, + subscriptionDetailsProps, + onSubscriptionCancel, + ...props +} = Astro.props; + +const subscriptionDetailsOptions = { + for: _for, + onSubscriptionCancel, + ...subscriptionDetailsProps, +}; + +let htmlElement = ''; + +if (asChild) { + htmlElement = await Astro.slots.render('default'); + htmlElement = addUnstyledAttributeToFirstTag(htmlElement, safeId); +} +--- + +{ + asChild ? ( + + ) : ( + + Subscription details + + ) +} + + diff --git a/packages/astro/src/react/CheckoutButton.tsx b/packages/astro/src/react/CheckoutButton.tsx new file mode 100644 index 00000000000..193e74263b4 --- /dev/null +++ b/packages/astro/src/react/CheckoutButton.tsx @@ -0,0 +1,53 @@ +import type { __experimental_CheckoutButtonProps } from '@clerk/types'; +import React from 'react'; + +import { assertSingleChild, normalizeWithDefaultValue, safeExecute, withClerk } from './utils'; +import type { WithClerkProp } from './utils'; + +export type { __experimental_CheckoutButtonProps as CheckoutButtonProps }; + +export const CheckoutButton = withClerk( + ({ clerk, children, ...props }: WithClerkProp>) => { + const { + planId, + planPeriod, + for: _for, + onSubscriptionComplete, + newSubscriptionRedirectUrl, + checkoutProps, + ...rest + } = props; + + // Note: Auth checks are moved to runtime since Astro React components + // don't have access to auth context at render time like Vue/React apps do + + children = normalizeWithDefaultValue(children, 'Checkout'); + const child = assertSingleChild(children)('CheckoutButton'); + + const clickHandler = () => { + if (!clerk) { + return; + } + + return clerk.__internal_openCheckout({ + planId, + planPeriod, + for: _for, + onSubscriptionComplete, + newSubscriptionRedirectUrl, + ...checkoutProps, + }); + }; + + const wrappedChildClickHandler: React.MouseEventHandler = async e => { + if (child && typeof child === 'object' && 'props' in child) { + await safeExecute(child.props.onClick)(e); + } + return clickHandler(); + }; + + const childProps = { ...rest, onClick: wrappedChildClickHandler }; + return React.cloneElement(child as React.ReactElement, childProps); + }, + 'CheckoutButton', +); diff --git a/packages/astro/src/react/PlanDetailsButton.tsx b/packages/astro/src/react/PlanDetailsButton.tsx new file mode 100644 index 00000000000..d4f1d8e2f72 --- /dev/null +++ b/packages/astro/src/react/PlanDetailsButton.tsx @@ -0,0 +1,40 @@ +import type { __experimental_PlanDetailsButtonProps } from '@clerk/types'; +import React from 'react'; + +import { assertSingleChild, normalizeWithDefaultValue, safeExecute, withClerk } from './utils'; +import type { WithClerkProp } from './utils'; + +export type { __experimental_PlanDetailsButtonProps as PlanDetailsButtonProps }; + +export const PlanDetailsButton = withClerk( + ({ clerk, children, ...props }: WithClerkProp>) => { + const { plan, planId, initialPlanPeriod, planDetailsProps, ...rest } = props; + + children = normalizeWithDefaultValue(children, 'Plan details'); + const child = assertSingleChild(children)('PlanDetailsButton'); + + const clickHandler = () => { + if (!clerk) { + return; + } + + return clerk.__internal_openPlanDetails({ + plan, + planId, + initialPlanPeriod, + ...planDetailsProps, + } as __experimental_PlanDetailsButtonProps); + }; + + const wrappedChildClickHandler: React.MouseEventHandler = async e => { + if (child && typeof child === 'object' && 'props' in child) { + await safeExecute(child.props.onClick)(e); + } + return clickHandler(); + }; + + const childProps = { ...rest, onClick: wrappedChildClickHandler }; + return React.cloneElement(child as React.ReactElement, childProps); + }, + 'PlanDetailsButton', +); diff --git a/packages/astro/src/react/SubscriptionDetailsButton.tsx b/packages/astro/src/react/SubscriptionDetailsButton.tsx new file mode 100644 index 00000000000..2038dc0387c --- /dev/null +++ b/packages/astro/src/react/SubscriptionDetailsButton.tsx @@ -0,0 +1,45 @@ +import type { __experimental_SubscriptionDetailsButtonProps } from '@clerk/types'; +import React from 'react'; + +import { assertSingleChild, normalizeWithDefaultValue, safeExecute, withClerk } from './utils'; +import type { WithClerkProp } from './utils'; + +export type { __experimental_SubscriptionDetailsButtonProps as SubscriptionDetailsButtonProps }; + +export const SubscriptionDetailsButton = withClerk( + ({ + clerk, + children, + ...props + }: WithClerkProp>) => { + const { for: _for, subscriptionDetailsProps, onSubscriptionCancel, ...rest } = props; + children = normalizeWithDefaultValue(children, 'Subscription details'); + const child = assertSingleChild(children)('SubscriptionDetailsButton'); + + // Note: Auth checks are moved to runtime since Astro React components + // don't have access to auth context at render time like Vue/React apps do + + const clickHandler = () => { + if (!clerk) { + return; + } + + return clerk.__internal_openSubscriptionDetails({ + for: _for, + onSubscriptionCancel, + ...subscriptionDetailsProps, + }); + }; + + const wrappedChildClickHandler: React.MouseEventHandler = async e => { + if (child && typeof child === 'object' && 'props' in child) { + await safeExecute(child.props.onClick)(e); + } + return clickHandler(); + }; + + const childProps = { ...rest, onClick: wrappedChildClickHandler }; + return React.cloneElement(child as React.ReactElement, childProps); + }, + 'SubscriptionDetailsButton', +); diff --git a/packages/astro/src/react/index.ts b/packages/astro/src/react/index.ts index 9b8cbf229f4..107e54c2fa5 100644 --- a/packages/astro/src/react/index.ts +++ b/packages/astro/src/react/index.ts @@ -1,10 +1,20 @@ import { SignInButton, type SignInButtonProps } from './SignInButton'; import { SignOutButton, type SignOutButtonProps } from './SignOutButton'; import { SignUpButton, type SignUpButtonProps } from './SignUpButton'; +import { SubscriptionDetailsButton, type SubscriptionDetailsButtonProps } from './SubscriptionDetailsButton'; +import { CheckoutButton, type CheckoutButtonProps } from './CheckoutButton'; +import { PlanDetailsButton, type PlanDetailsButtonProps } from './PlanDetailsButton'; export * from './uiComponents'; export * from './controlComponents'; export * from './hooks'; -export { SignInButton, SignOutButton, SignUpButton }; +export { SignInButton, SignOutButton, SignUpButton, SubscriptionDetailsButton, CheckoutButton, PlanDetailsButton }; -export type { SignInButtonProps, SignOutButtonProps, SignUpButtonProps }; +export type { + SignInButtonProps, + SignOutButtonProps, + SignUpButtonProps, + SubscriptionDetailsButtonProps, + CheckoutButtonProps, + PlanDetailsButtonProps, +}; diff --git a/packages/astro/src/react/utils.tsx b/packages/astro/src/react/utils.tsx index f52fdb769ce..7a0b2f617af 100644 --- a/packages/astro/src/react/utils.tsx +++ b/packages/astro/src/react/utils.tsx @@ -49,7 +49,16 @@ export type WithClerkProp = T & { // TODO-SHARED: Duplicate from @clerk/clerk-react export const assertSingleChild = (children: React.ReactNode) => - (name: 'SignInButton' | 'SignUpButton' | 'SignOutButton' | 'SignInWithMetamaskButton') => { + ( + name: + | 'SignInButton' + | 'SignUpButton' + | 'SignOutButton' + | 'SignInWithMetamaskButton' + | 'SubscriptionDetailsButton' + | 'CheckoutButton' + | 'PlanDetailsButton', + ) => { try { return React.Children.only(children); } catch { diff --git a/packages/vue/src/components/CheckoutButton.vue b/packages/vue/src/components/CheckoutButton.vue new file mode 100644 index 00000000000..c056d943c4e --- /dev/null +++ b/packages/vue/src/components/CheckoutButton.vue @@ -0,0 +1,53 @@ + + + diff --git a/packages/vue/src/components/PlanDetailsButton.vue b/packages/vue/src/components/PlanDetailsButton.vue new file mode 100644 index 00000000000..ba094c41412 --- /dev/null +++ b/packages/vue/src/components/PlanDetailsButton.vue @@ -0,0 +1,40 @@ + + + diff --git a/packages/vue/src/components/SubscriptionDetailsButton.vue b/packages/vue/src/components/SubscriptionDetailsButton.vue new file mode 100644 index 00000000000..5dec21240ac --- /dev/null +++ b/packages/vue/src/components/SubscriptionDetailsButton.vue @@ -0,0 +1,50 @@ + + + diff --git a/packages/vue/src/components/index.ts b/packages/vue/src/components/index.ts index 37827c8ad8a..bc0110098ae 100644 --- a/packages/vue/src/components/index.ts +++ b/packages/vue/src/components/index.ts @@ -28,3 +28,6 @@ export { default as SignInButton } from './SignInButton.vue'; export { default as SignUpButton } from './SignUpButton.vue'; export { default as SignOutButton } from './SignOutButton.vue'; export { default as SignInWithMetamaskButton } from './SignInWithMetamaskButton.vue'; +export { default as SubscriptionDetailsButton } from './SubscriptionDetailsButton.vue'; +export { default as CheckoutButton } from './CheckoutButton.vue'; +export { default as PlanDetailsButton } from './PlanDetailsButton.vue'; diff --git a/packages/vue/src/index.ts b/packages/vue/src/index.ts index 325f66ea890..5d6c74fb8ca 100644 --- a/packages/vue/src/index.ts +++ b/packages/vue/src/index.ts @@ -8,5 +8,11 @@ export * from './composables'; export { clerkPlugin, type PluginOptions } from './plugin'; export { updateClerkOptions } from './utils'; +export type { + __experimental_SubscriptionDetailsButtonProps as SubscriptionDetailsButtonProps, + __experimental_CheckoutButtonProps as CheckoutButtonProps, + __experimental_PlanDetailsButtonProps as PlanDetailsButtonProps, +} from '@clerk/types'; + setErrorThrowerOptions({ packageName: PACKAGE_NAME }); setClerkJsLoadingErrorPackageName(PACKAGE_NAME); diff --git a/packages/vue/src/utils/childrenUtils.ts b/packages/vue/src/utils/childrenUtils.ts index 55b76b61c14..9cacf87b0db 100644 --- a/packages/vue/src/utils/childrenUtils.ts +++ b/packages/vue/src/utils/childrenUtils.ts @@ -3,7 +3,14 @@ import { h, Text, type VNode } from 'vue'; import { errorThrower } from '../errors/errorThrower'; import { multipleChildrenInButtonComponent } from '../errors/messages'; -type ButtonName = 'SignInButton' | 'SignUpButton' | 'SignOutButton' | 'SignInWithMetamaskButton'; +type ButtonName = + | 'SignInButton' + | 'SignUpButton' + | 'SignOutButton' + | 'SignInWithMetamaskButton' + | 'SubscriptionDetailsButton' + | 'CheckoutButton' + | 'PlanDetailsButton'; export const normalizeWithDefaultValue = (slotContent: VNode[] | undefined, defaultValue: string) => { // Render a button with the default value if no slot content is provided From 15631976f21085260781b031106b6042548b8ed0 Mon Sep 17 00:00:00 2001 From: panteliselef Date: Wed, 20 Aug 2025 16:05:01 +0300 Subject: [PATCH 2/8] add tests --- .../src/pages/billing/checkout-btn.astro | 18 ++++++++++++++++++ .../src/pages/billing/plan-details-btn.astro | 12 ++++++++++++ .../billing/subscription-details-btn.astro | 12 ++++++++++++ integration/templates/vue-vite/src/router.ts | 16 ++++++++++++++++ .../vue-vite/src/views/billing/CheckoutBtn.vue | 16 ++++++++++++++++ .../src/views/billing/PlanDetailsBtn.vue | 9 +++++++++ .../views/billing/SubscriptionDetailsBtn.vue | 9 +++++++++ integration/tests/pricing-table.test.ts | 14 -------------- 8 files changed, 92 insertions(+), 14 deletions(-) create mode 100644 integration/templates/astro-node/src/pages/billing/checkout-btn.astro create mode 100644 integration/templates/astro-node/src/pages/billing/plan-details-btn.astro create mode 100644 integration/templates/astro-node/src/pages/billing/subscription-details-btn.astro create mode 100644 integration/templates/vue-vite/src/views/billing/CheckoutBtn.vue create mode 100644 integration/templates/vue-vite/src/views/billing/PlanDetailsBtn.vue create mode 100644 integration/templates/vue-vite/src/views/billing/SubscriptionDetailsBtn.vue diff --git a/integration/templates/astro-node/src/pages/billing/checkout-btn.astro b/integration/templates/astro-node/src/pages/billing/checkout-btn.astro new file mode 100644 index 00000000000..9f3e10b2a32 --- /dev/null +++ b/integration/templates/astro-node/src/pages/billing/checkout-btn.astro @@ -0,0 +1,18 @@ +--- +import { SignedIn } from '@clerk/astro/components'; +import { CheckoutButton } from '@clerk/astro/components'; +import Layout from '../../layouts/Layout.astro'; +--- + + +
+ + + Checkout Now + + +
+
diff --git a/integration/templates/astro-node/src/pages/billing/plan-details-btn.astro b/integration/templates/astro-node/src/pages/billing/plan-details-btn.astro new file mode 100644 index 00000000000..85dda8f4be5 --- /dev/null +++ b/integration/templates/astro-node/src/pages/billing/plan-details-btn.astro @@ -0,0 +1,12 @@ +--- +import { PlanDetailsButton } from '@clerk/astro/components'; +import Layout from '../../layouts/Layout.astro'; +--- + + +
+ + Plan details + +
+
diff --git a/integration/templates/astro-node/src/pages/billing/subscription-details-btn.astro b/integration/templates/astro-node/src/pages/billing/subscription-details-btn.astro new file mode 100644 index 00000000000..f871fa33a03 --- /dev/null +++ b/integration/templates/astro-node/src/pages/billing/subscription-details-btn.astro @@ -0,0 +1,12 @@ +--- +import { SubscriptionDetailsButton } from '@clerk/astro/components'; +import Layout from '../../layouts/Layout.astro'; +--- + + +
+ + Subscription details + +
+
diff --git a/integration/templates/vue-vite/src/router.ts b/integration/templates/vue-vite/src/router.ts index c598b2b0bff..31fc822e18e 100644 --- a/integration/templates/vue-vite/src/router.ts +++ b/integration/templates/vue-vite/src/router.ts @@ -47,6 +47,22 @@ const routes = [ path: '/user', component: () => import('./views/Profile.vue'), }, + // Billing button routes + { + name: 'CheckoutBtn', + path: '/billing/checkout-btn', + component: () => import('./views/billing/CheckoutBtn.vue'), + }, + { + name: 'PlanDetailsBtn', + path: '/billing/plan-details-btn', + component: () => import('./views/billing/PlanDetailsBtn.vue'), + }, + { + name: 'SubscriptionDetailsBtn', + path: '/billing/subscription-details-btn', + component: () => import('./views/billing/SubscriptionDetailsBtn.vue'), + }, ]; const router = createRouter({ diff --git a/integration/templates/vue-vite/src/views/billing/CheckoutBtn.vue b/integration/templates/vue-vite/src/views/billing/CheckoutBtn.vue new file mode 100644 index 00000000000..c8713f51ad8 --- /dev/null +++ b/integration/templates/vue-vite/src/views/billing/CheckoutBtn.vue @@ -0,0 +1,16 @@ + + + diff --git a/integration/templates/vue-vite/src/views/billing/PlanDetailsBtn.vue b/integration/templates/vue-vite/src/views/billing/PlanDetailsBtn.vue new file mode 100644 index 00000000000..55446772fd3 --- /dev/null +++ b/integration/templates/vue-vite/src/views/billing/PlanDetailsBtn.vue @@ -0,0 +1,9 @@ + + + diff --git a/integration/templates/vue-vite/src/views/billing/SubscriptionDetailsBtn.vue b/integration/templates/vue-vite/src/views/billing/SubscriptionDetailsBtn.vue new file mode 100644 index 00000000000..e6fb55dc6a3 --- /dev/null +++ b/integration/templates/vue-vite/src/views/billing/SubscriptionDetailsBtn.vue @@ -0,0 +1,9 @@ + + + diff --git a/integration/tests/pricing-table.test.ts b/integration/tests/pricing-table.test.ts index 783ff3e7ada..3a07c1b0b22 100644 --- a/integration/tests/pricing-table.test.ts +++ b/integration/tests/pricing-table.test.ts @@ -29,10 +29,6 @@ testAgainstRunningApps({ withEnv: [appConfigs.envs.withBilling] })('pricing tabl }); test('renders pricing details of a specific plan', async ({ page, context }) => { - if (!app.name.includes('next')) { - return; - } - const u = createTestUtils({ app, page, context }); await u.po.page.goToRelative('/billing/plan-details-btn'); @@ -62,10 +58,6 @@ testAgainstRunningApps({ withEnv: [appConfigs.envs.withBilling] })('pricing tabl }); test('when signed in, clicking get started button opens checkout drawer', async ({ page, context }) => { - if (!app.name.includes('next')) { - return; - } - const u = createTestUtils({ app, page, context }); await u.po.signIn.goTo(); await u.po.signIn.signInWithEmailAndInstantPassword({ email: fakeUser.email, password: fakeUser.password }); @@ -92,9 +84,6 @@ testAgainstRunningApps({ withEnv: [appConfigs.envs.withBilling] })('pricing tabl }); test('when signed in, clicking checkout button open checkout drawer', async ({ page, context }) => { - if (!app.name.includes('next')) { - return; - } const u = createTestUtils({ app, page, context }); await u.po.signIn.goTo(); await u.po.signIn.signInWithEmailAndInstantPassword({ email: fakeUser.email, password: fakeUser.password }); @@ -129,9 +118,6 @@ testAgainstRunningApps({ withEnv: [appConfigs.envs.withBilling] })('pricing tabl }); test('Displays subscription details drawer', async ({ page, context }) => { - if (!app.name.includes('next')) { - return; - } const u = createTestUtils({ app, page, context }); await u.po.signIn.goTo(); await u.po.signIn.signInWithEmailAndInstantPassword({ email: fakeUser.email, password: fakeUser.password }); From 86b0d3be16b859bdb7f8799f7f0fc596238400ae Mon Sep 17 00:00:00 2001 From: panteliselef Date: Wed, 20 Aug 2025 16:12:19 +0300 Subject: [PATCH 3/8] mark as experimental --- .../src/pages/billing/checkout-btn.astro | 3 +-- .../src/pages/billing/plan-details-btn.astro | 2 +- .../billing/subscription-details-btn.astro | 2 +- .../vue-vite/src/views/billing/CheckoutBtn.vue | 3 ++- .../src/views/billing/PlanDetailsBtn.vue | 2 +- .../views/billing/SubscriptionDetailsBtn.vue | 2 +- packages/astro/src/astro-components/index.ts | 6 +++--- packages/astro/src/react/index.ts | 17 +++++++++++------ packages/vue/package.json | 4 ++++ packages/vue/src/components/index.ts | 3 --- packages/vue/src/experimental.ts | 9 +++++++++ packages/vue/src/index.ts | 6 ------ packages/vue/tsup.config.ts | 6 +++--- 13 files changed, 37 insertions(+), 28 deletions(-) create mode 100644 packages/vue/src/experimental.ts diff --git a/integration/templates/astro-node/src/pages/billing/checkout-btn.astro b/integration/templates/astro-node/src/pages/billing/checkout-btn.astro index 9f3e10b2a32..736992e6033 100644 --- a/integration/templates/astro-node/src/pages/billing/checkout-btn.astro +++ b/integration/templates/astro-node/src/pages/billing/checkout-btn.astro @@ -1,6 +1,5 @@ --- -import { SignedIn } from '@clerk/astro/components'; -import { CheckoutButton } from '@clerk/astro/components'; +import { SignedIn, __experimental_CheckoutButton as CheckoutButton } from '@clerk/astro/components'; import Layout from '../../layouts/Layout.astro'; --- diff --git a/integration/templates/astro-node/src/pages/billing/plan-details-btn.astro b/integration/templates/astro-node/src/pages/billing/plan-details-btn.astro index 85dda8f4be5..ed35edd3d51 100644 --- a/integration/templates/astro-node/src/pages/billing/plan-details-btn.astro +++ b/integration/templates/astro-node/src/pages/billing/plan-details-btn.astro @@ -1,5 +1,5 @@ --- -import { PlanDetailsButton } from '@clerk/astro/components'; +import { __experimental_PlanDetailsButton as PlanDetailsButton } from '@clerk/astro/components'; import Layout from '../../layouts/Layout.astro'; --- diff --git a/integration/templates/astro-node/src/pages/billing/subscription-details-btn.astro b/integration/templates/astro-node/src/pages/billing/subscription-details-btn.astro index f871fa33a03..b8233ffb34a 100644 --- a/integration/templates/astro-node/src/pages/billing/subscription-details-btn.astro +++ b/integration/templates/astro-node/src/pages/billing/subscription-details-btn.astro @@ -1,5 +1,5 @@ --- -import { SubscriptionDetailsButton } from '@clerk/astro/components'; +import { __experimental_SubscriptionDetailsButton as SubscriptionDetailsButton } from '@clerk/astro/components'; import Layout from '../../layouts/Layout.astro'; --- diff --git a/integration/templates/vue-vite/src/views/billing/CheckoutBtn.vue b/integration/templates/vue-vite/src/views/billing/CheckoutBtn.vue index c8713f51ad8..39c23365733 100644 --- a/integration/templates/vue-vite/src/views/billing/CheckoutBtn.vue +++ b/integration/templates/vue-vite/src/views/billing/CheckoutBtn.vue @@ -12,5 +12,6 @@ diff --git a/integration/templates/vue-vite/src/views/billing/PlanDetailsBtn.vue b/integration/templates/vue-vite/src/views/billing/PlanDetailsBtn.vue index 55446772fd3..cc51a1035bc 100644 --- a/integration/templates/vue-vite/src/views/billing/PlanDetailsBtn.vue +++ b/integration/templates/vue-vite/src/views/billing/PlanDetailsBtn.vue @@ -5,5 +5,5 @@ diff --git a/integration/templates/vue-vite/src/views/billing/SubscriptionDetailsBtn.vue b/integration/templates/vue-vite/src/views/billing/SubscriptionDetailsBtn.vue index e6fb55dc6a3..5ff010ab10c 100644 --- a/integration/templates/vue-vite/src/views/billing/SubscriptionDetailsBtn.vue +++ b/integration/templates/vue-vite/src/views/billing/SubscriptionDetailsBtn.vue @@ -5,5 +5,5 @@ diff --git a/packages/astro/src/astro-components/index.ts b/packages/astro/src/astro-components/index.ts index 970c1f45cf9..1fa8b1f554a 100644 --- a/packages/astro/src/astro-components/index.ts +++ b/packages/astro/src/astro-components/index.ts @@ -12,9 +12,9 @@ export { default as AuthenticateWithRedirectCallback } from './control/Authentic export { default as SignInButton } from './unstyled/SignInButton.astro'; export { default as SignUpButton } from './unstyled/SignUpButton.astro'; export { default as SignOutButton } from './unstyled/SignOutButton.astro'; -export { default as SubscriptionDetailsButton } from './unstyled/SubscriptionDetailsButton.astro'; -export { default as CheckoutButton } from './unstyled/CheckoutButton.astro'; -export { default as PlanDetailsButton } from './unstyled/PlanDetailsButton.astro'; +export { default as __experimental_SubscriptionDetailsButton } from './unstyled/SubscriptionDetailsButton.astro'; +export { default as __experimental_CheckoutButton } from './unstyled/CheckoutButton.astro'; +export { default as __experimental_PlanDetailsButton } from './unstyled/PlanDetailsButton.astro'; /** * UI Components diff --git a/packages/astro/src/react/index.ts b/packages/astro/src/react/index.ts index 107e54c2fa5..c16086cc435 100644 --- a/packages/astro/src/react/index.ts +++ b/packages/astro/src/react/index.ts @@ -1,20 +1,25 @@ +import { CheckoutButton, type CheckoutButtonProps } from './CheckoutButton'; +import { PlanDetailsButton, type PlanDetailsButtonProps } from './PlanDetailsButton'; import { SignInButton, type SignInButtonProps } from './SignInButton'; import { SignOutButton, type SignOutButtonProps } from './SignOutButton'; import { SignUpButton, type SignUpButtonProps } from './SignUpButton'; import { SubscriptionDetailsButton, type SubscriptionDetailsButtonProps } from './SubscriptionDetailsButton'; -import { CheckoutButton, type CheckoutButtonProps } from './CheckoutButton'; -import { PlanDetailsButton, type PlanDetailsButtonProps } from './PlanDetailsButton'; export * from './uiComponents'; export * from './controlComponents'; export * from './hooks'; -export { SignInButton, SignOutButton, SignUpButton, SubscriptionDetailsButton, CheckoutButton, PlanDetailsButton }; +export { SignInButton, SignOutButton, SignUpButton }; +export { + SubscriptionDetailsButton as __experimental_SubscriptionDetailsButton, + CheckoutButton as __experimental_CheckoutButton, + PlanDetailsButton as __experimental_PlanDetailsButton, +}; export type { SignInButtonProps, SignOutButtonProps, SignUpButtonProps, - SubscriptionDetailsButtonProps, - CheckoutButtonProps, - PlanDetailsButtonProps, + SubscriptionDetailsButtonProps as __experimental_SubscriptionDetailsButtonProps, + CheckoutButtonProps as __experimental_CheckoutButtonProps, + PlanDetailsButtonProps as __experimental_PlanDetailsButtonProps, }; diff --git a/packages/vue/package.json b/packages/vue/package.json index e5903dbd883..133db439ba2 100644 --- a/packages/vue/package.json +++ b/packages/vue/package.json @@ -30,6 +30,10 @@ "types": "./dist/index.d.ts", "default": "./dist/index.js" }, + "./experimental": { + "types": "./dist/experimental.d.ts", + "default": "./dist/experimental.js" + }, "./internal": { "types": "./dist/internal.d.ts", "default": "./dist/internal.js" diff --git a/packages/vue/src/components/index.ts b/packages/vue/src/components/index.ts index bc0110098ae..37827c8ad8a 100644 --- a/packages/vue/src/components/index.ts +++ b/packages/vue/src/components/index.ts @@ -28,6 +28,3 @@ export { default as SignInButton } from './SignInButton.vue'; export { default as SignUpButton } from './SignUpButton.vue'; export { default as SignOutButton } from './SignOutButton.vue'; export { default as SignInWithMetamaskButton } from './SignInWithMetamaskButton.vue'; -export { default as SubscriptionDetailsButton } from './SubscriptionDetailsButton.vue'; -export { default as CheckoutButton } from './CheckoutButton.vue'; -export { default as PlanDetailsButton } from './PlanDetailsButton.vue'; diff --git a/packages/vue/src/experimental.ts b/packages/vue/src/experimental.ts new file mode 100644 index 00000000000..e5814f1ec07 --- /dev/null +++ b/packages/vue/src/experimental.ts @@ -0,0 +1,9 @@ +export { default as SubscriptionDetailsButton } from './components/SubscriptionDetailsButton.vue'; +export { default as CheckoutButton } from './components/CheckoutButton.vue'; +export { default as PlanDetailsButton } from './components/PlanDetailsButton.vue'; + +export type { + __experimental_SubscriptionDetailsButtonProps as SubscriptionDetailsButtonProps, + __experimental_CheckoutButtonProps as CheckoutButtonProps, + __experimental_PlanDetailsButtonProps as PlanDetailsButtonProps, +} from '@clerk/types'; diff --git a/packages/vue/src/index.ts b/packages/vue/src/index.ts index 5d6c74fb8ca..325f66ea890 100644 --- a/packages/vue/src/index.ts +++ b/packages/vue/src/index.ts @@ -8,11 +8,5 @@ export * from './composables'; export { clerkPlugin, type PluginOptions } from './plugin'; export { updateClerkOptions } from './utils'; -export type { - __experimental_SubscriptionDetailsButtonProps as SubscriptionDetailsButtonProps, - __experimental_CheckoutButtonProps as CheckoutButtonProps, - __experimental_PlanDetailsButtonProps as PlanDetailsButtonProps, -} from '@clerk/types'; - setErrorThrowerOptions({ packageName: PACKAGE_NAME }); setClerkJsLoadingErrorPackageName(PACKAGE_NAME); diff --git a/packages/vue/tsup.config.ts b/packages/vue/tsup.config.ts index c5c1a246a46..fe7ddaac46c 100644 --- a/packages/vue/tsup.config.ts +++ b/packages/vue/tsup.config.ts @@ -9,7 +9,7 @@ type EsbuildPlugin = NonNullable[number]; export default defineConfig(() => { return { clean: true, - entry: ['./src/index.ts', './src/internal.ts', './src/errors.ts'], + entry: ['./src/index.ts', './src/experimental.ts', './src/internal.ts', './src/errors.ts'], format: ['esm'], bundle: true, sourcemap: true, @@ -17,13 +17,13 @@ export default defineConfig(() => { dts: false, esbuildPlugins: [ // Adds .vue files support - vuePlugin() as EsbuildPlugin, + vuePlugin(), // Automatically generates runtime props from TypeScript types/interfaces for all // control and UI components, adding them to Vue components during build via // Object.defineProperty autoPropsPlugin({ include: ['**/*.ts'], - }) as EsbuildPlugin, + }), ], define: { PACKAGE_NAME: `"${name}"`, From faa409ccbd491dfb679f9d4b195f4cc7fa727b46 Mon Sep 17 00:00:00 2001 From: panteliselef Date: Wed, 20 Aug 2025 16:35:11 +0300 Subject: [PATCH 4/8] add changeset --- .changeset/cold-parks-push.md | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 .changeset/cold-parks-push.md diff --git a/.changeset/cold-parks-push.md b/.changeset/cold-parks-push.md new file mode 100644 index 00000000000..b720789abec --- /dev/null +++ b/.changeset/cold-parks-push.md @@ -0,0 +1,6 @@ +--- +'@clerk/astro': minor +'@clerk/vue': minor +--- + +Expose billing buttons as experimental From 5224ca17df4fc0cf416e07f2eae62ab9ad3bc90c Mon Sep 17 00:00:00 2001 From: panteliselef Date: Wed, 20 Aug 2025 16:46:55 +0300 Subject: [PATCH 5/8] skip astro tests --- integration/tests/pricing-table.test.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/integration/tests/pricing-table.test.ts b/integration/tests/pricing-table.test.ts index 3a07c1b0b22..be2ce4d558e 100644 --- a/integration/tests/pricing-table.test.ts +++ b/integration/tests/pricing-table.test.ts @@ -29,6 +29,8 @@ testAgainstRunningApps({ withEnv: [appConfigs.envs.withBilling] })('pricing tabl }); test('renders pricing details of a specific plan', async ({ page, context }) => { + test.skip(app.name.includes('astro'), 'Still working on it'); + const u = createTestUtils({ app, page, context }); await u.po.page.goToRelative('/billing/plan-details-btn'); @@ -84,6 +86,7 @@ testAgainstRunningApps({ withEnv: [appConfigs.envs.withBilling] })('pricing tabl }); test('when signed in, clicking checkout button open checkout drawer', async ({ page, context }) => { + test.skip(app.name.includes('astro'), 'Still working on it'); const u = createTestUtils({ app, page, context }); await u.po.signIn.goTo(); await u.po.signIn.signInWithEmailAndInstantPassword({ email: fakeUser.email, password: fakeUser.password }); @@ -118,6 +121,7 @@ testAgainstRunningApps({ withEnv: [appConfigs.envs.withBilling] })('pricing tabl }); test('Displays subscription details drawer', async ({ page, context }) => { + test.skip(app.name.includes('astro'), 'Still working on it'); const u = createTestUtils({ app, page, context }); await u.po.signIn.goTo(); await u.po.signIn.signInWithEmailAndInstantPassword({ email: fakeUser.email, password: fakeUser.password }); From 070e81698a1aa694bd447ea84840a808af5e53e8 Mon Sep 17 00:00:00 2001 From: panteliselef Date: Wed, 20 Aug 2025 16:53:42 +0300 Subject: [PATCH 6/8] add experimental tags --- packages/vue/src/experimental.ts | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/packages/vue/src/experimental.ts b/packages/vue/src/experimental.ts index e5814f1ec07..18816459452 100644 --- a/packages/vue/src/experimental.ts +++ b/packages/vue/src/experimental.ts @@ -1,9 +1,41 @@ +/** + * @experimental + * These components and their prop types are unstable and may change in future releases. + * They are part of Clerk's Billing feature which is available under public beta. + */ export { default as SubscriptionDetailsButton } from './components/SubscriptionDetailsButton.vue'; + +/** + * @experimental + * These components and their prop types are unstable and may change in future releases. + * They are part of Clerk's Billing feature which is available under public beta. + */ export { default as CheckoutButton } from './components/CheckoutButton.vue'; + +/** + * @experimental + * These components and their prop types are unstable and may change in future releases. + * They are part of Clerk's Billing feature which is available under public beta. + */ export { default as PlanDetailsButton } from './components/PlanDetailsButton.vue'; export type { + /** + * @experimental + * These components and their prop types are unstable and may change in future releases. + * They are part of Clerk's Billing feature which is available under public beta. + */ __experimental_SubscriptionDetailsButtonProps as SubscriptionDetailsButtonProps, + /** + * @experimental + * These components and their prop types are unstable and may change in future releases. + * They are part of Clerk's Billing feature which is available under public beta. + */ __experimental_CheckoutButtonProps as CheckoutButtonProps, + /** + * @experimental + * These components and their prop types are unstable and may change in future releases. + * They are part of Clerk's Billing feature which is available under public beta. + */ __experimental_PlanDetailsButtonProps as PlanDetailsButtonProps, } from '@clerk/types'; From 183001fe8f25b965fa25400c8f73908537178b22 Mon Sep 17 00:00:00 2001 From: panteliselef Date: Wed, 20 Aug 2025 17:11:44 +0300 Subject: [PATCH 7/8] fix lint --- .../astro-components/unstyled/PlanDetailsButton.astro | 10 +--------- .../unstyled/SubscriptionDetailsButton.astro | 6 ++++-- packages/astro/src/react/CheckoutButton.tsx | 6 +++--- packages/astro/src/react/PlanDetailsButton.tsx | 6 +++--- packages/astro/src/react/SubscriptionDetailsButton.tsx | 6 +++--- 5 files changed, 14 insertions(+), 20 deletions(-) diff --git a/packages/astro/src/astro-components/unstyled/PlanDetailsButton.astro b/packages/astro/src/astro-components/unstyled/PlanDetailsButton.astro index 6c102ef73a5..090c6d6f3c0 100644 --- a/packages/astro/src/astro-components/unstyled/PlanDetailsButton.astro +++ b/packages/astro/src/astro-components/unstyled/PlanDetailsButton.astro @@ -14,15 +14,7 @@ if ('as' in Astro.props) { logAsPropUsageDeprecation(); } -const { - as: Tag = 'button', - asChild, - plan, - planId, - initialPlanPeriod, - planDetailsProps, - ...props -} = Astro.props; +const { as: Tag = 'button', asChild, plan, planId, initialPlanPeriod, planDetailsProps, ...props } = Astro.props; const planDetailsOptions = { plan, diff --git a/packages/astro/src/astro-components/unstyled/SubscriptionDetailsButton.astro b/packages/astro/src/astro-components/unstyled/SubscriptionDetailsButton.astro index 90b13578819..c81d8ae6c3f 100644 --- a/packages/astro/src/astro-components/unstyled/SubscriptionDetailsButton.astro +++ b/packages/astro/src/astro-components/unstyled/SubscriptionDetailsButton.astro @@ -1,10 +1,12 @@ --- -import type { HTMLTag, Polymorphic } from 'astro/types'; import type { __experimental_SubscriptionDetailsButtonProps } from '@clerk/types'; + +import type { HTMLTag, Polymorphic } from 'astro/types'; import type { ButtonProps } from '../../types'; import { addUnstyledAttributeToFirstTag, logAsPropUsageDeprecation } from './utils'; -type Props = Polymorphic> & __experimental_SubscriptionDetailsButtonProps; +type Props = Polymorphic> & + __experimental_SubscriptionDetailsButtonProps; import { generateSafeId } from '@clerk/astro/internal'; diff --git a/packages/astro/src/react/CheckoutButton.tsx b/packages/astro/src/react/CheckoutButton.tsx index 193e74263b4..20e8cd7991b 100644 --- a/packages/astro/src/react/CheckoutButton.tsx +++ b/packages/astro/src/react/CheckoutButton.tsx @@ -1,8 +1,8 @@ import type { __experimental_CheckoutButtonProps } from '@clerk/types'; import React from 'react'; -import { assertSingleChild, normalizeWithDefaultValue, safeExecute, withClerk } from './utils'; import type { WithClerkProp } from './utils'; +import { assertSingleChild, normalizeWithDefaultValue, safeExecute, withClerk } from './utils'; export type { __experimental_CheckoutButtonProps as CheckoutButtonProps }; @@ -39,9 +39,9 @@ export const CheckoutButton = withClerk( }); }; - const wrappedChildClickHandler: React.MouseEventHandler = async e => { + const wrappedChildClickHandler: React.MouseEventHandler = e => { if (child && typeof child === 'object' && 'props' in child) { - await safeExecute(child.props.onClick)(e); + void safeExecute(child.props.onClick)(e); } return clickHandler(); }; diff --git a/packages/astro/src/react/PlanDetailsButton.tsx b/packages/astro/src/react/PlanDetailsButton.tsx index d4f1d8e2f72..a6b43316e5e 100644 --- a/packages/astro/src/react/PlanDetailsButton.tsx +++ b/packages/astro/src/react/PlanDetailsButton.tsx @@ -1,8 +1,8 @@ import type { __experimental_PlanDetailsButtonProps } from '@clerk/types'; import React from 'react'; -import { assertSingleChild, normalizeWithDefaultValue, safeExecute, withClerk } from './utils'; import type { WithClerkProp } from './utils'; +import { assertSingleChild, normalizeWithDefaultValue, safeExecute, withClerk } from './utils'; export type { __experimental_PlanDetailsButtonProps as PlanDetailsButtonProps }; @@ -26,9 +26,9 @@ export const PlanDetailsButton = withClerk( } as __experimental_PlanDetailsButtonProps); }; - const wrappedChildClickHandler: React.MouseEventHandler = async e => { + const wrappedChildClickHandler: React.MouseEventHandler = e => { if (child && typeof child === 'object' && 'props' in child) { - await safeExecute(child.props.onClick)(e); + void safeExecute(child.props.onClick)(e); } return clickHandler(); }; diff --git a/packages/astro/src/react/SubscriptionDetailsButton.tsx b/packages/astro/src/react/SubscriptionDetailsButton.tsx index 2038dc0387c..c737a72a909 100644 --- a/packages/astro/src/react/SubscriptionDetailsButton.tsx +++ b/packages/astro/src/react/SubscriptionDetailsButton.tsx @@ -1,8 +1,8 @@ import type { __experimental_SubscriptionDetailsButtonProps } from '@clerk/types'; import React from 'react'; -import { assertSingleChild, normalizeWithDefaultValue, safeExecute, withClerk } from './utils'; import type { WithClerkProp } from './utils'; +import { assertSingleChild, normalizeWithDefaultValue, safeExecute, withClerk } from './utils'; export type { __experimental_SubscriptionDetailsButtonProps as SubscriptionDetailsButtonProps }; @@ -31,9 +31,9 @@ export const SubscriptionDetailsButton = withClerk( }); }; - const wrappedChildClickHandler: React.MouseEventHandler = async e => { + const wrappedChildClickHandler: React.MouseEventHandler = e => { if (child && typeof child === 'object' && 'props' in child) { - await safeExecute(child.props.onClick)(e); + void safeExecute(child.props.onClick)(e); } return clickHandler(); }; From 2fdb49b2f985752ce81c6a66a3678eacb0d53035 Mon Sep 17 00:00:00 2001 From: panteliselef Date: Wed, 20 Aug 2025 18:18:53 +0300 Subject: [PATCH 8/8] rename the test --- .../astro-node/src/pages/billing/plan-details-btn.astro | 2 +- integration/tests/pricing-table.test.ts | 2 +- packages/astro/src/astro-components/index.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/integration/templates/astro-node/src/pages/billing/plan-details-btn.astro b/integration/templates/astro-node/src/pages/billing/plan-details-btn.astro index ed35edd3d51..85dda8f4be5 100644 --- a/integration/templates/astro-node/src/pages/billing/plan-details-btn.astro +++ b/integration/templates/astro-node/src/pages/billing/plan-details-btn.astro @@ -1,5 +1,5 @@ --- -import { __experimental_PlanDetailsButton as PlanDetailsButton } from '@clerk/astro/components'; +import { PlanDetailsButton } from '@clerk/astro/components'; import Layout from '../../layouts/Layout.astro'; --- diff --git a/integration/tests/pricing-table.test.ts b/integration/tests/pricing-table.test.ts index be2ce4d558e..a74cb99c434 100644 --- a/integration/tests/pricing-table.test.ts +++ b/integration/tests/pricing-table.test.ts @@ -29,7 +29,7 @@ testAgainstRunningApps({ withEnv: [appConfigs.envs.withBilling] })('pricing tabl }); test('renders pricing details of a specific plan', async ({ page, context }) => { - test.skip(app.name.includes('astro'), 'Still working on it'); + // test.skip(app.name.includes('astro'), 'Still working on it'); const u = createTestUtils({ app, page, context }); await u.po.page.goToRelative('/billing/plan-details-btn'); diff --git a/packages/astro/src/astro-components/index.ts b/packages/astro/src/astro-components/index.ts index 1fa8b1f554a..7f017e6fc6d 100644 --- a/packages/astro/src/astro-components/index.ts +++ b/packages/astro/src/astro-components/index.ts @@ -14,7 +14,7 @@ export { default as SignUpButton } from './unstyled/SignUpButton.astro'; export { default as SignOutButton } from './unstyled/SignOutButton.astro'; export { default as __experimental_SubscriptionDetailsButton } from './unstyled/SubscriptionDetailsButton.astro'; export { default as __experimental_CheckoutButton } from './unstyled/CheckoutButton.astro'; -export { default as __experimental_PlanDetailsButton } from './unstyled/PlanDetailsButton.astro'; +export { default as PlanDetailsButton } from './unstyled/PlanDetailsButton.astro'; /** * UI Components