-
Notifications
You must be signed in to change notification settings - Fork 408
feat(clerk-js,shared): Make subscription actions more visible with inline buttons #7255
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
feat(clerk-js,shared): Make subscription actions more visible with inline buttons #7255
Conversation
🦋 Changeset detectedLatest commit: 9da5e48 The changes in this PR will be included in the next version bump. This PR includes changesets to release 22 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
WalkthroughReplaced the three‑dots menu with inline action buttons rendered in a dedicated actions row in SubscriptionDetails; added two new appearance keys and corresponding ElementsConfig fields; updated unit and integration tests to interact with inline controls instead of a dropdown. Changes
Sequence Diagram(s)sequenceDiagram
participant UI as SubscriptionCard (UI)
participant Guard as BillingPermissionGuard
participant Hooks as BillingHooks
participant Checkout as CheckoutService
participant API as BillingAPI
rect rgba(235,245,255,0.6)
UI->>Guard: render (permission check)
Guard-->>UI: allowed / denied
end
alt Click inline "Switch Plan"
UI->>Hooks: handleSwitchPlan(planId)
Hooks->>Checkout: startCheckout(planId)
Checkout-->>UI: redirect / checkout flow
else Click inline "Cancel"
UI->>Hooks: handleCancelSubscription(subId)
Hooks->>API: cancelSubscription(subId)
API-->>UI: success / failure
else Click inline "Resubscribe"
UI->>Hooks: handleResubscribe(planId)
Hooks->>Checkout: startCheckout(planId)
Checkout-->>UI: redirect / checkout flow
end
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes
Poem
Pre-merge checks and finishing touches✅ Passed checks (3 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
Comment |
23edb7d to
ba8adcc
Compare
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.
Actionable comments posted: 0
🧹 Nitpick comments (1)
packages/clerk-js/src/ui/components/SubscriptionDetails/index.tsx (1)
400-487: Handlers/permissions and explicit buttons are clean; consider tightening switchability conditionThe new action handlers (
handleSwitchPlan,handleCancelSubscription,handleResubscribe) are well-factored, have accurate hook dependencies, and reuse the existing checkout + revalidation flow correctly. ThecanManageBillingguard also cleanly prevents rendering or usage by users without billing permissions, and the new Flex layout withsubscriptionDetailsActionButton/subscriptionDetailsCancelButtondescriptors fits the appearance model nicely.One behavioral nuance to consider:
isSwitchablecurrently ignorescanceledAt, so a canceled, non-free subscription that’s still instatus !== 'past_due'could surface both “Switch plan” and “Re-subscribe” buttons simultaneously. If that’s not desired UX, you might want to gate switching on an active (or at least non‑canceled) subscription, e.g.:- const isSwitchable = - ((subscription.planPeriod === 'month' && Boolean(subscription.plan.annualMonthlyFee)) || - subscription.planPeriod === 'annual') && - subscription.status !== 'past_due'; + const isSwitchable = + subscription.canceledAt === null && + ((subscription.planPeriod === 'month' && Boolean(subscription.plan.annualMonthlyFee)) || + subscription.planPeriod === 'annual') && + subscription.status !== 'past_due';
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Disabled knowledge base sources:
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (3)
packages/clerk-js/src/ui/components/SubscriptionDetails/index.tsx(2 hunks)packages/clerk-js/src/ui/customizables/elementDescriptors.ts(1 hunks)packages/shared/src/types/appearance.ts(1 hunks)
🔇 Additional comments (3)
packages/clerk-js/src/ui/customizables/elementDescriptors.ts (1)
502-510: New subscription details button descriptors are correctly wired
subscriptionDetailsActionButtonandsubscriptionDetailsCancelButtonare added consistently with the other subscription details keys and will be type-checked againstElementsConfig, so the descriptor map stays in sync with the appearance surface.packages/shared/src/types/appearance.ts (1)
637-645: ElementsConfig extensions for subscription buttons look consistent
subscriptionDetailsActionButtonandsubscriptionDetailsCancelButtonare added with the sameWithOptionsshape as the surrounding subscription card entries and match the descriptor keys, so theme authors can now independently style the primary and destructive actions.packages/clerk-js/src/ui/components/SubscriptionDetails/index.tsx (1)
609-610: SubscriptionCardActions placement within the card looks appropriateRendering
SubscriptionCardActionsas the final section of the card after the detail rows keeps the structure consistent (header, details, actions) and aligns with the new border-separated actions row styling.
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.
Actionable comments posted: 0
🧹 Nitpick comments (1)
packages/clerk-js/src/ui/components/SubscriptionDetails/index.tsx (1)
459-486: Actions bar composition is solid; consider a more stable key (optional)The Flex-based actions row with its own descriptor, border, and spacing cleanly separates actions from the card body and uses the new appearance descriptors for primary vs destructive buttons correctly. Since the action set is small and order is fixed,
key={index}is acceptable, but if actions ever become dynamic, consider a more stable key source (e.g., an explicitidon each action) to avoid potential React reconciliation quirks.
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Disabled knowledge base sources:
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (1)
packages/clerk-js/src/ui/components/SubscriptionDetails/index.tsx(4 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
packages/clerk-js/src/ui/components/SubscriptionDetails/index.tsx (2)
packages/clerk-js/src/ui/customizables/index.ts (1)
Flex(16-16)packages/clerk-js/src/ui/customizables/elementDescriptors.ts (1)
descriptors(581-581)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
- GitHub Check: Formatting | Dedupe | Changeset
- GitHub Check: Build Packages
- GitHub Check: Analyze (javascript-typescript)
- GitHub Check: semgrep-cloud-platform/scan
- GitHub Check: semgrep-cloud-platform/scan
🔇 Additional comments (3)
packages/clerk-js/src/ui/components/SubscriptionDetails/index.tsx (3)
24-24: Context import usage looks correct and cohesiveImporting
SubscriberTypeContext,usePlansContext,useSubscriberTypeContext, anduseSubscriptionfrom the shared../../contextsentry keeps all subscription-related context coming from a single module and matches their usage throughout this file. No changes needed here.
399-412: Switch-plan action labels and checkout wiring look consistentThe new labels
billing.switchToAnnual/billing.switchToMonthlycorrectly branch onsubscription.planPeriodand keep the action semantics clear while the checkout call flips the period as expected (month → annual,annual → month) and passes the currentplanIdandforscope. As long as the new i18n keys exist in the billing namespace, this logic is solid.
608-610: PlacingSubscriptionCardActionsat the bottom of the card aligns with the details layoutRendering
SubscriptionCardActionsafter the status-specificDetailRows ensures the action bar is consistently at the bottom of each card and leverages the internalis*guards so empty action sets result in no footer UI. This composition reads clearly and doesn’t introduce new edge cases.
@clerk/agent-toolkit
@clerk/astro
@clerk/backend
@clerk/chrome-extension
@clerk/clerk-js
@clerk/dev-cli
@clerk/elements
@clerk/clerk-expo
@clerk/expo-passkeys
@clerk/express
@clerk/fastify
@clerk/localizations
@clerk/nextjs
@clerk/nuxt
@clerk/clerk-react
@clerk/react-router
@clerk/remix
@clerk/shared
@clerk/tanstack-react-start
@clerk/testing
@clerk/themes
@clerk/types
@clerk/upgrade
@clerk/vue
commit: |
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.
Actionable comments posted: 0
🧹 Nitpick comments (1)
packages/clerk-js/src/ui/components/SubscriptionDetails/__tests__/SubscriptionDetails.test.tsx (1)
36-1232: Consider adding explicit assertion for menu removal.While the tests thoroughly validate the new inline button UI, consider adding an explicit assertion in one or more test cases to verify that the three-dots menu button is no longer present. This would provide clearer documentation that the menu-based UI was intentionally removed.
Example assertion you could add to any test case:
await waitFor(() => { expect(getByText('Switch to annual')).toBeVisible(); expect(getByText('Cancel subscription')).toBeVisible(); + expect(queryByRole('button', { name: /open menu/i })).toBeNull(); });
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Disabled knowledge base sources:
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (2)
integration/tests/pricing-table.test.ts(0 hunks)packages/clerk-js/src/ui/components/SubscriptionDetails/__tests__/SubscriptionDetails.test.tsx(5 hunks)
💤 Files with no reviewable changes (1)
- integration/tests/pricing-table.test.ts
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (33)
- GitHub Check: Integration Tests (nextjs, chrome, 16)
- GitHub Check: Integration Tests (custom, chrome)
- GitHub Check: Integration Tests (quickstart, chrome, 16)
- GitHub Check: Integration Tests (nextjs, chrome, 15)
- GitHub Check: Integration Tests (billing, chrome, RQ)
- GitHub Check: Integration Tests (billing, chrome)
- GitHub Check: Integration Tests (vue, chrome)
- GitHub Check: Integration Tests (nextjs, chrome, 15, RQ)
- GitHub Check: Integration Tests (machine, chrome)
- GitHub Check: Integration Tests (quickstart, chrome, 15)
- GitHub Check: Integration Tests (tanstack-react-start, chrome)
- GitHub Check: Integration Tests (sessions:staging, chrome)
- GitHub Check: Integration Tests (nextjs, chrome, 14)
- GitHub Check: Integration Tests (expo-web, chrome)
- GitHub Check: Integration Tests (localhost, chrome)
- GitHub Check: Integration Tests (handshake, chrome)
- GitHub Check: Integration Tests (nuxt, chrome)
- GitHub Check: Integration Tests (sessions, chrome)
- GitHub Check: Integration Tests (handshake:staging, chrome)
- GitHub Check: Integration Tests (react-router, chrome)
- GitHub Check: Integration Tests (express, chrome)
- GitHub Check: Integration Tests (ap-flows, chrome)
- GitHub Check: Integration Tests (generic, chrome)
- GitHub Check: Integration Tests (astro, chrome)
- GitHub Check: Integration Tests (elements, chrome)
- GitHub Check: Unit Tests (22, **)
- GitHub Check: Publish with pkg-pr-new
- GitHub Check: Static analysis
- GitHub Check: Unit Tests (22, shared, clerk-js, RQ)
- GitHub Check: Formatting | Dedupe | Changeset
- GitHub Check: Analyze (javascript-typescript)
- GitHub Check: semgrep-cloud-platform/scan
- GitHub Check: semgrep-cloud-platform/scan
🔇 Additional comments (5)
packages/clerk-js/src/ui/components/SubscriptionDetails/__tests__/SubscriptionDetails.test.tsx (5)
134-136: LGTM - Inline button text assertions updated correctly.The test now validates the simplified button text for inline actions, which aligns with the new UI design where subscription actions are directly visible rather than hidden in a menu.
237-239: LGTM - Consistent inline button validation.The annual subscription test correctly mirrors the monthly subscription pattern, validating simplified action button text.
314-317: LGTM - Good defensive test for free subscription actions.These assertions properly verify that free subscriptions don't display cancel or switch action buttons, which is the expected behavior.
467-470: LGTM - Proper action button count verification.The test correctly verifies that "Cancel subscription" appears exactly once for the active subscription, while also checking for the "Switch to monthly" option. The count assertion is a good practice to prevent duplicate inline actions.
1121-1124: LGTM - Free trial action button properly validated.The test correctly verifies that free trial subscriptions show the specific "Cancel free trial" button text, which appropriately distinguishes free trials from regular subscriptions.
228e609 to
516676b
Compare
This PR updates the styling of the action buttons related to Billing subscriptions. Previously, these actions were hidden behind a three-dots menu, which made them hard to find. Now, the buttons are visible outside of the menu, making them much easier to spot.
adc060d to
a04513e
Compare
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.
Actionable comments posted: 0
🧹 Nitpick comments (2)
packages/clerk-js/src/ui/components/SubscriptionDetails/__tests__/SubscriptionDetails.test.tsx (2)
697-714: Consider more explicit button selection.Using
getAllByTextwith array indexing[0]and[length-1]works but is somewhat fragile—it assumes DOM order and could break if additional buttons with the same text appear.Consider adding
data-testidattributes or usinggetByRole('button', { name: /cancel subscription/i })with container scoping to distinguish between the inline trigger and the confirmation dialog button more explicitly.Example approach:
-const cancelButtons = getAllByText('Cancel subscription'); -await userEvent.click(cancelButtons[0]); +const cancelButton = getByRole('button', { name: /^Cancel subscription$/i }); +await userEvent.click(cancelButton);Then use a scoped query for the dialog:
-const allCancelButtons = getAllByText('Cancel subscription'); -await userEvent.click(allCancelButtons[allCancelButtons.length - 1]); +const dialog = getByRole('dialog'); +const confirmButton = within(dialog).getByRole('button', { name: /^Cancel subscription$/i }); +await userEvent.click(confirmButton);
1217-1235: Same array-indexing pattern for free trial cancellation.The same fragility concern applies here as in the regular subscription cancellation test (lines 697-714). Consider using role-based queries with container scoping for more robust button selection.
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Disabled knowledge base sources:
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (6)
.changeset/curvy-pianos-wait.md(1 hunks)integration/tests/pricing-table.test.ts(2 hunks)packages/clerk-js/src/ui/components/SubscriptionDetails/__tests__/SubscriptionDetails.test.tsx(18 hunks)packages/clerk-js/src/ui/components/SubscriptionDetails/index.tsx(4 hunks)packages/clerk-js/src/ui/customizables/elementDescriptors.ts(1 hunks)packages/shared/src/types/appearance.ts(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (4)
- integration/tests/pricing-table.test.ts
- packages/clerk-js/src/ui/customizables/elementDescriptors.ts
- .changeset/curvy-pianos-wait.md
- packages/clerk-js/src/ui/components/SubscriptionDetails/index.tsx
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (29)
- GitHub Check: Integration Tests (nextjs, chrome, 14)
- GitHub Check: Integration Tests (nextjs, chrome, 15, RQ)
- GitHub Check: Integration Tests (quickstart, chrome, 15)
- GitHub Check: Integration Tests (nextjs, chrome, 16)
- GitHub Check: Integration Tests (handshake, chrome)
- GitHub Check: Integration Tests (quickstart, chrome, 16)
- GitHub Check: Integration Tests (react-router, chrome)
- GitHub Check: Integration Tests (nextjs, chrome, 15)
- GitHub Check: Integration Tests (express, chrome)
- GitHub Check: Integration Tests (billing, chrome, RQ)
- GitHub Check: Integration Tests (billing, chrome)
- GitHub Check: Integration Tests (machine, chrome)
- GitHub Check: Integration Tests (custom, chrome)
- GitHub Check: Integration Tests (handshake:staging, chrome)
- GitHub Check: Integration Tests (nuxt, chrome)
- GitHub Check: Integration Tests (expo-web, chrome)
- GitHub Check: Integration Tests (vue, chrome)
- GitHub Check: Integration Tests (tanstack-react-start, chrome)
- GitHub Check: Integration Tests (astro, chrome)
- GitHub Check: Integration Tests (localhost, chrome)
- GitHub Check: Integration Tests (sessions, chrome)
- GitHub Check: Integration Tests (generic, chrome)
- GitHub Check: Integration Tests (sessions:staging, chrome)
- GitHub Check: Integration Tests (elements, chrome)
- GitHub Check: Integration Tests (ap-flows, chrome)
- GitHub Check: Unit Tests (22, shared, clerk-js, RQ)
- GitHub Check: Unit Tests (22, **)
- GitHub Check: Publish with pkg-pr-new
- GitHub Check: Static analysis
🔇 Additional comments (4)
packages/shared/src/types/appearance.ts (1)
644-645: LGTM! Clean addition of new appearance keys.The new
subscriptionDetailsActionButtonandsubscriptionDetailsCancelButtonfields follow the existingElementsConfigpattern and are properly grouped with related subscription detail keys.packages/clerk-js/src/ui/components/SubscriptionDetails/__tests__/SubscriptionDetails.test.tsx (3)
134-135: Inline button assertions correctly updated.The tests now properly verify that action buttons ("Switch to annual", "Switch to monthly", "Cancel subscription") are rendered inline rather than hidden behind a menu.
Also applies to: 237-238
315-316: Correct verification that free plans have no action buttons.Properly confirms that free plans do not display cancel or switch actions inline.
467-469: Inline action buttons correctly verified across scenarios.Tests properly validate that "Switch to monthly", "Resubscribe", "Cancel subscription", and "Cancel free trial" buttons appear inline as expected.
Also applies to: 1126-1129
| price: normalizeFormatted(subscription.plan.fee.amountFormatted), | ||
| currency: subscription.plan.fee.currencySymbol, | ||
| }), | ||
| ? localizationKeys('billing.switchToAnnual') |
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.
Can't we keep the labels that include the prices ? Originally we didn't had them, and the based of feedback we added them.
If we are deciding to remove them, we should mark those localization keys as deprecated and remove them in the next major, since they are not longer used.
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.
Restored prices in switch plans buttons via b507f9a
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.
Actionable comments posted: 0
🧹 Nitpick comments (1)
packages/clerk-js/src/ui/components/SubscriptionDetails/index.tsx (1)
473-500: Consider using stable identifiers instead of array index as React keysThe inline action layout and use of
subscriptionDetailsCardActions/subscriptionDetails(Action|Cancel)Buttondescriptors look good and match the intent of surfacing actions more clearly.One improvement:
key={index}on the mappedactionscan lead to subtle UI/state issues if the set of actions changes over time (e.g. when a subscription becomes cancellable or re‑subscribable). Prefer a stable key derived from the action type, for example by adding atype: 'switch' | 'cancel' | 'reSubscribe'field when constructing theactionsarray and using that as thekey.
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Disabled knowledge base sources:
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (2)
packages/clerk-js/src/ui/components/SubscriptionDetails/__tests__/SubscriptionDetails.test.tsx(16 hunks)packages/clerk-js/src/ui/components/SubscriptionDetails/index.tsx(2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- packages/clerk-js/src/ui/components/SubscriptionDetails/tests/SubscriptionDetails.test.tsx
🧰 Additional context used
🧬 Code graph analysis (1)
packages/clerk-js/src/ui/components/SubscriptionDetails/index.tsx (2)
packages/clerk-js/src/ui/customizables/index.ts (1)
Flex(16-16)packages/clerk-js/src/ui/customizables/elementDescriptors.ts (1)
descriptors(581-581)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
- GitHub Check: Formatting | Dedupe | Changeset
- GitHub Check: Build Packages
- GitHub Check: Analyze (javascript-typescript)
- GitHub Check: semgrep-cloud-platform/scan
- GitHub Check: semgrep-cloud-platform/scan
🔇 Additional comments (1)
packages/clerk-js/src/ui/components/SubscriptionDetails/index.tsx (1)
623-624: LGTM on rendering actions as a dedicated footer rowPlacing
<SubscriptionCardActions>at the bottom of the card, separated from pricing and details, makes the available actions much more discoverable while reusing the shared cancellation flow. No issues spotted here.
Description
This PR updates the styling of the action buttons related to Billing subscriptions.
Previously, these actions were hidden behind a three-dots menu, which made them hard to find.
Now, the buttons are visible outside of the menu, making them much easier to spot.
Checklist
pnpm testruns as expected.pnpm buildruns as expected.Type of change
Summary by CodeRabbit
New Features
Bug Fixes / Improvements
Chores
✏️ Tip: You can customize this high-level summary in your review settings.