Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/legal-ants-flow.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@clerk/clerk-js': patch
---

Add class `cl-planDetails-root` to the parent div element that containes the plan details drawer.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Fix typo and tighten wording in changeset entry

Correct “containes” → “contains” and simplify phrasing.

Apply this diff:

-Add class `cl-planDetails-root` to the parent div element that containes the plan details drawer.
+Add CSS class `cl-planDetails-root` to the parent div that contains the plan details drawer.
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
Add class `cl-planDetails-root` to the parent div element that containes the plan details drawer.
Add CSS class `cl-planDetails-root` to the parent div that contains the plan details drawer.
🧰 Tools
🪛 LanguageTool

[grammar] ~5-~5: There might be a mistake here.
Context: ... that containes the plan details drawer.

(QB_NEW_EN)

🤖 Prompt for AI Agents
In .changeset/legal-ants-flow.md around line 5, the changeset entry has a typo
and awkward phrasing: replace “containes” with “contains” and simplify the
sentence to read something like: "Add class `cl-planDetails-root` to the parent
div that contains the plan details drawer." Update the line accordingly to fix
the spelling and tighten the wording.

5 changes: 5 additions & 0 deletions .changeset/little-seals-hang.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@clerk/testing': patch
---

Add `planDetails` to the page details object.
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { SignedIn } from '@clerk/nextjs';
import { CheckoutButton } from '@clerk/nextjs/experimental';

export default function Home() {
return (
<main>
<SignedIn>
<CheckoutButton
planId='cplan_2wMjqdlza0hTJc4HLCoBwAiExhF'
planPeriod='month'
>
Checkout Now
</CheckoutButton>
</SignedIn>
</main>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { PlanDetailsButton } from '@clerk/nextjs/experimental';

export default function Home() {
return (
<main>
<PlanDetailsButton planId='cplan_2wMjqdlza0hTJc4HLCoBwAiExhF'>Plan details</PlanDetailsButton>
</main>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { SubscriptionDetailsButton } from '@clerk/nextjs/experimental';

export default function Home() {
return (
<main>
<SubscriptionDetailsButton>Subscription details</SubscriptionDetailsButton>
</main>
);
}
61 changes: 61 additions & 0 deletions integration/tests/pricing-table.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,29 @@ testAgainstRunningApps({ withEnv: [appConfigs.envs.withBilling] })('pricing tabl
await expect(u.po.page.getByRole('heading', { name: 'Pro' })).toBeVisible();
});

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');

await u.po.page.getByRole('button', { name: 'Plan details' }).click();

await u.po.planDetails.waitForMounted();
const { root } = u.po.planDetails;
await expect(root.getByRole('heading', { name: 'Plus' })).toBeVisible();
await expect(root.getByText('$9.99')).toBeVisible();
await expect(root.getByText('This is the Plus plan!')).toBeVisible();
await expect(root.getByText('Feature One')).toBeVisible();
await expect(root.getByText('First feature')).toBeVisible();
await expect(root.getByText('Feature Two')).toBeVisible();
await expect(root.getByText('Second feature')).toBeVisible();
await expect(root.getByText('Feature Three')).toBeVisible();
await expect(root.getByText('Third feature')).toBeVisible();
});

test('when signed out, clicking subscribe button navigates to sign in page', async ({ page, context }) => {
const u = createTestUtils({ app, page, context });
await u.po.page.goToRelative('/pricing-table');
Expand All @@ -39,6 +62,10 @@ 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 });
Expand All @@ -64,6 +91,21 @@ testAgainstRunningApps({ withEnv: [appConfigs.envs.withBilling] })('pricing tabl
await expect(u.po.page.getByText('Checkout')).toBeVisible();
});

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 });
await u.po.page.goToRelative('/billing/checkout-btn');

await u.po.page.getByRole('button', { name: 'Checkout Now' }).click();
await u.po.checkout.waitForMounted();
await u.po.page.getByText(/^Checkout$/).click();
await u.po.checkout.fillTestCard();
});

test('can subscribe to a plan', async ({ page, context }) => {
const u = createTestUtils({ app, page, context });
await u.po.signIn.goTo();
Expand All @@ -86,6 +128,25 @@ 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 });
await u.po.page.goToRelative('/billing/subscription-details-btn');

await u.po.page.getByRole('button', { name: 'Subscription details' }).click();

await u.po.subscriptionDetails.waitForMounted();
await expect(u.po.page.getByText('Plus')).toBeVisible();
await expect(u.po.page.getByText('Subscription details')).toBeVisible();
await expect(u.po.page.getByText('Current billing cycle')).toBeVisible();
await expect(u.po.page.getByText('Next payment on')).toBeVisible();
await expect(u.po.page.getByText('Next payment amount')).toBeVisible();
});

test('can upgrade to a new plan with saved card', async ({ page, context }) => {
const u = createTestUtils({ app, page, context });
await u.po.signIn.goTo();
Expand Down
11 changes: 8 additions & 3 deletions packages/clerk-js/src/ui/components/Plans/PlanDetails.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import {
Col,
descriptors,
Flex,
Flow,
Heading,
localizationKeys,
Span,
Expand All @@ -30,9 +31,13 @@ import {

export const PlanDetails = (props: __internal_PlanDetailsProps) => {
return (
<Drawer.Content>
<PlanDetailsInternal {...props} />
</Drawer.Content>
<Flow.Root flow='planDetails'>
<Flow.Part>
<Drawer.Content>
<PlanDetailsInternal {...props} />
</Drawer.Content>
</Flow.Part>
</Flow.Root>
);
};

Expand Down
1 change: 1 addition & 0 deletions packages/clerk-js/src/ui/elements/contexts/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ export type FlowMetadata = {
| 'apiKeys'
| 'oauthConsent'
| 'subscriptionDetails'
| 'subscriptionDetails'
| 'taskChooseOrganization';
part?:
| 'start'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { createExpectPageObject } from './expect';
import { createImpersonationPageObject } from './impersonation';
import { createKeylessPopoverPageObject } from './keylessPopover';
import { createOrganizationSwitcherComponentPageObject } from './organizationSwitcher';
import { createPlanDetailsPageObject } from './planDetails';
import { createPricingTablePageObject } from './pricingTable';
import { createSessionTaskComponentPageObject } from './sessionTask';
import { createSignInComponentPageObject } from './signIn';
Expand Down Expand Up @@ -50,5 +51,6 @@ export const createPageObjects = ({
waitlist: createWaitlistComponentPageObject(testArgs),
apiKeys: createAPIKeysComponentPageObject(testArgs),
subscriptionDetails: createSubscriptionDetailsPageObject(testArgs),
planDetails: createPlanDetailsPageObject(testArgs),
};
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import type { EnhancedPage } from './app';
import { common } from './common';

export const createPlanDetailsPageObject = (testArgs: { page: EnhancedPage }) => {
const { page } = testArgs;
const self = {
...common(testArgs),
waitForMounted: (selector = '.cl-planDetails-root') => {
return page.waitForSelector(selector, { state: 'attached' });
},
root: page.locator('.cl-planDetails-root'),
};
return self;
};
Loading