mirror of
https://github.com/bitwarden/browser
synced 2026-02-19 02:44:01 +00:00
* Implement the required changes * Fix the family plan creation for expired sub * Resolve the pr comments * resolve the resubscribe issue * Removed redirectOnCompletion: true from the resubscribe * Display the Change payment method dialog on the subscription page * adjust the page reload time * revert payment method open in subscription page * Enable cancel premium see the subscription page * Revert the removal of hasPremiumPersonally * remove extra space * Add can view subscription * Use the canViewSubscription * Resolve the tab default to premium * use the subscription Instead of hasPremium * Revert the changes on user-subscription * Use the flag to redirect to subscription page * revert the canViewSubscription change * resolve the route issue with premium * Change the path to * Revert the previous iteration changes * Fix the build error
461 lines
13 KiB
Plaintext
461 lines
13 KiB
Plaintext
import { Meta, Story, Canvas } from "@storybook/addon-docs/blocks";
|
|
import * as SubscriptionCardStories from "./subscription-card.component.stories";
|
|
|
|
<Meta of={SubscriptionCardStories} />
|
|
|
|
# Subscription Card
|
|
|
|
A comprehensive UI component for displaying subscription status, payment details, and contextual
|
|
action prompts based on subscription state. Dynamically adapts its presentation based on the
|
|
subscription status (active, trialing, incomplete, past due, canceled, unpaid, etc.).
|
|
|
|
<Canvas of={SubscriptionCardStories.Active} />
|
|
|
|
## Table of Contents
|
|
|
|
- [Usage](#usage)
|
|
- [API](#api)
|
|
- [Inputs](#inputs)
|
|
- [Outputs](#outputs)
|
|
- [Data Structure](#data-structure)
|
|
- [Subscription States](#subscription-states)
|
|
- [Design](#design)
|
|
- [Examples](#examples)
|
|
- [Active](#active)
|
|
- [Active With Upgrade](#active-with-upgrade)
|
|
- [Trial](#trial)
|
|
- [Trial With Upgrade](#trial-with-upgrade)
|
|
- [Incomplete Payment](#incomplete-payment)
|
|
- [Incomplete Expired](#incomplete-expired)
|
|
- [Past Due](#past-due)
|
|
- [Pending Cancellation](#pending-cancellation)
|
|
- [Unpaid](#unpaid)
|
|
- [Canceled](#canceled)
|
|
- [Enterprise](#enterprise)
|
|
- [Features](#features)
|
|
- [Do's and Don'ts](#dos-and-donts)
|
|
- [Accessibility](#accessibility)
|
|
|
|
## Usage
|
|
|
|
The subscription card component is designed to display comprehensive subscription information on
|
|
billing pages, account management interfaces, and subscription dashboards.
|
|
|
|
```ts
|
|
import { SubscriptionCardComponent, BitwardenSubscription } from "@bitwarden/subscription";
|
|
```
|
|
|
|
```html
|
|
<billing-subscription-card
|
|
[title]="'Premium Subscription'"
|
|
[subscription]="subscription"
|
|
[showUpgradeButton]="false"
|
|
(callToActionClicked)="handleAction($event)"
|
|
>
|
|
</billing-subscription-card>
|
|
```
|
|
|
|
## API
|
|
|
|
### Inputs
|
|
|
|
| Input | Type | Description |
|
|
| ------------------- | ----------------------- | ----------------------------------------------------------------------- |
|
|
| `title` | `string` | **Required.** The title displayed at the top of the card |
|
|
| `subscription` | `BitwardenSubscription` | **Required.** The subscription data including status, cart, and storage |
|
|
| `showUpgradeButton` | `boolean` | **Optional.** Whether to show the upgrade callout (default: `false`) |
|
|
|
|
### Outputs
|
|
|
|
| Output | Type | Description |
|
|
| --------------------- | ------------------------ | ---------------------------------------------------------- |
|
|
| `callToActionClicked` | `SubscriptionCardAction` | Emitted when a user clicks an action button in the callout |
|
|
|
|
**SubscriptionCardAction Type:**
|
|
|
|
```typescript
|
|
type SubscriptionCardAction =
|
|
| "contact-support"
|
|
| "manage-invoices"
|
|
| "reinstate-subscription"
|
|
| "resubscribe"
|
|
| "update-payment"
|
|
| "upgrade-plan";
|
|
```
|
|
|
|
## Data Structure
|
|
|
|
The component uses the `BitwardenSubscription` type, which is a discriminated union based on status:
|
|
|
|
```typescript
|
|
type BitwardenSubscription = HasCart & HasStorage & (Suspension | Billable | Canceled);
|
|
|
|
type HasCart = {
|
|
cart: Cart; // From @bitwarden/pricing
|
|
};
|
|
|
|
type HasStorage = {
|
|
storage: {
|
|
available: number;
|
|
readableUsed: string;
|
|
used: number;
|
|
};
|
|
};
|
|
|
|
type Suspension = {
|
|
status: "incomplete" | "incomplete_expired" | "past_due" | "unpaid";
|
|
suspension: Date;
|
|
gracePeriod: number;
|
|
};
|
|
|
|
type Billable = {
|
|
status: "trialing" | "active";
|
|
nextCharge: Date;
|
|
cancelAt?: Date;
|
|
};
|
|
|
|
type Canceled = {
|
|
status: "canceled";
|
|
canceled: Date;
|
|
};
|
|
```
|
|
|
|
## Subscription States
|
|
|
|
The component dynamically adapts its appearance and calls-to-action based on the subscription
|
|
status:
|
|
|
|
- **active**: Subscription is active and paid up
|
|
- **trialing**: Subscription is in trial period
|
|
- **incomplete**: Payment failed, requires action
|
|
- **incomplete_expired**: Payment issue expired, subscription suspended
|
|
- **past_due**: Payment overdue but within grace period
|
|
- **unpaid**: Subscription suspended due to non-payment
|
|
- **canceled**: Subscription was canceled
|
|
|
|
Each state displays an appropriate badge, callout message, and relevant action buttons.
|
|
|
|
## Design
|
|
|
|
The component follows the Bitwarden design system with:
|
|
|
|
- **Status Badge**: Color-coded badges (success, warning, danger) indicating subscription state
|
|
- **Cart Summary**: Integrated cart summary showing pricing details
|
|
- **Contextual Callouts**: Warning/info/danger callouts with appropriate actions
|
|
- **Modern Angular**: Uses signal inputs (`input.required`, `input`) and `computed` signals
|
|
- **OnPush Change Detection**: Optimized performance with change detection strategy
|
|
- **Typography**: Consistent text styling using the typography module
|
|
- **Tailwind CSS**: Uses `tw-` prefixed utility classes for styling
|
|
- **Responsive Layout**: Flexbox-based layout that adapts to container size
|
|
|
|
## Examples
|
|
|
|
### Active
|
|
|
|
Standard active subscription with regular billing:
|
|
|
|
<Canvas of={SubscriptionCardStories.Active} />
|
|
|
|
```html
|
|
<billing-subscription-card
|
|
[title]="'Premium Subscription'"
|
|
[subscription]="{
|
|
status: 'active',
|
|
nextCharge: new Date('2025-02-15'),
|
|
cart: {
|
|
passwordManager: {
|
|
seats: {
|
|
quantity: 1,
|
|
name: 'members',
|
|
cost: 10.00
|
|
}
|
|
},
|
|
cadence: 'annually',
|
|
estimatedTax: 2.71
|
|
},
|
|
storage: {
|
|
available: 1000,
|
|
used: 234,
|
|
readableUsed: '234 MB'
|
|
}
|
|
}"
|
|
(callToActionClicked)="handleAction($event)"
|
|
>
|
|
</billing-subscription-card>
|
|
```
|
|
|
|
### Active With Upgrade
|
|
|
|
Active subscription with upgrade promotion callout:
|
|
|
|
<Canvas of={SubscriptionCardStories.ActiveWithUpgrade} />
|
|
|
|
```html
|
|
<billing-subscription-card
|
|
[title]="'Premium Subscription'"
|
|
[subscription]="activeSubscription"
|
|
[showUpgradeButton]="true"
|
|
(callToActionClicked)="handleAction($event)"
|
|
>
|
|
</billing-subscription-card>
|
|
```
|
|
|
|
### Trial
|
|
|
|
Subscription in trial period showing next charge date:
|
|
|
|
<Canvas of={SubscriptionCardStories.Trial} />
|
|
|
|
```html
|
|
<billing-subscription-card
|
|
[title]="'Premium Subscription'"
|
|
[subscription]="{
|
|
status: 'trialing',
|
|
nextCharge: new Date('2025-02-01'),
|
|
cart: {...},
|
|
storage: {...}
|
|
}"
|
|
(callToActionClicked)="handleAction($event)"
|
|
>
|
|
</billing-subscription-card>
|
|
```
|
|
|
|
### Trial With Upgrade
|
|
|
|
Trial subscription with upgrade option displayed:
|
|
|
|
<Canvas of={SubscriptionCardStories.TrialWithUpgrade} />
|
|
|
|
```html
|
|
<billing-subscription-card
|
|
[title]="'Premium Subscription'"
|
|
[subscription]="trialSubscription"
|
|
[showUpgradeButton]="true"
|
|
(callToActionClicked)="handleAction($event)"
|
|
>
|
|
</billing-subscription-card>
|
|
```
|
|
|
|
### Incomplete Payment
|
|
|
|
Payment failed, showing warning with update payment action:
|
|
|
|
<Canvas of={SubscriptionCardStories.Incomplete} />
|
|
|
|
```html
|
|
<billing-subscription-card
|
|
[title]="'Premium Subscription'"
|
|
[subscription]="{
|
|
status: 'incomplete',
|
|
suspension: new Date('2025-02-15'),
|
|
gracePeriod: 7,
|
|
cart: {...},
|
|
storage: {...}
|
|
}"
|
|
(callToActionClicked)="handleAction($event)"
|
|
>
|
|
</billing-subscription-card>
|
|
```
|
|
|
|
**Actions available:** Update Payment, Contact Support
|
|
|
|
### Incomplete Expired
|
|
|
|
Payment issue expired, subscription has been suspended:
|
|
|
|
<Canvas of={SubscriptionCardStories.IncompleteExpired} />
|
|
|
|
```html
|
|
<billing-subscription-card
|
|
[title]="'Premium Subscription'"
|
|
[subscription]="{
|
|
status: 'incomplete_expired',
|
|
suspension: new Date('2025-01-01'),
|
|
gracePeriod: 0,
|
|
cart: {...},
|
|
storage: {...}
|
|
}"
|
|
(callToActionClicked)="handleAction($event)"
|
|
>
|
|
</billing-subscription-card>
|
|
```
|
|
|
|
**Actions available:** Resubscribe
|
|
|
|
### Past Due
|
|
|
|
Payment past due with active grace period:
|
|
|
|
<Canvas of={SubscriptionCardStories.PastDue} />
|
|
|
|
```html
|
|
<billing-subscription-card
|
|
[title]="'Premium Subscription'"
|
|
[subscription]="{
|
|
status: 'past_due',
|
|
suspension: new Date('2025-02-05'),
|
|
gracePeriod: 14,
|
|
cart: {...},
|
|
storage: {...}
|
|
}"
|
|
(callToActionClicked)="handleAction($event)"
|
|
>
|
|
</billing-subscription-card>
|
|
```
|
|
|
|
**Actions available:** Manage Invoices
|
|
|
|
### Pending Cancellation
|
|
|
|
Active subscription scheduled to be canceled:
|
|
|
|
<Canvas of={SubscriptionCardStories.PendingCancellation} />
|
|
|
|
```html
|
|
<billing-subscription-card
|
|
[title]="'Premium Subscription'"
|
|
[subscription]="{
|
|
status: 'active',
|
|
nextCharge: new Date('2025-02-15'),
|
|
cancelAt: new Date('2025-03-01'),
|
|
cart: {...},
|
|
storage: {...}
|
|
}"
|
|
(callToActionClicked)="handleAction($event)"
|
|
>
|
|
</billing-subscription-card>
|
|
```
|
|
|
|
**Actions available:** Reinstate Subscription
|
|
|
|
### Unpaid
|
|
|
|
Subscription suspended due to unpaid invoices:
|
|
|
|
<Canvas of={SubscriptionCardStories.Unpaid} />
|
|
|
|
```html
|
|
<billing-subscription-card
|
|
[title]="'Premium Subscription'"
|
|
[subscription]="{
|
|
status: 'unpaid',
|
|
suspension: new Date('2025-01-20'),
|
|
gracePeriod: 0,
|
|
cart: {...},
|
|
storage: {...}
|
|
}"
|
|
(callToActionClicked)="handleAction($event)"
|
|
>
|
|
</billing-subscription-card>
|
|
```
|
|
|
|
**Actions available:** Manage Invoices
|
|
|
|
### Canceled
|
|
|
|
Subscription that has been canceled:
|
|
|
|
<Canvas of={SubscriptionCardStories.Canceled} />
|
|
|
|
```html
|
|
<billing-subscription-card
|
|
[title]="'Premium Subscription'"
|
|
[subscription]="{
|
|
status: 'canceled',
|
|
canceled: new Date('2025-01-15'),
|
|
cart: {...},
|
|
storage: {...}
|
|
}"
|
|
(callToActionClicked)="handleAction($event)"
|
|
>
|
|
</billing-subscription-card>
|
|
```
|
|
|
|
**Actions available:** Resubscribe
|
|
|
|
### Enterprise
|
|
|
|
Enterprise subscription with multiple products and discount:
|
|
|
|
<Canvas of={SubscriptionCardStories.Enterprise} />
|
|
|
|
```html
|
|
<billing-subscription-card
|
|
[title]="'Enterprise Subscription'"
|
|
[subscription]="{
|
|
status: 'active',
|
|
nextCharge: new Date('2025-03-01'),
|
|
cart: {
|
|
passwordManager: {
|
|
seats: { quantity: 5, name: 'members', cost: 7 },
|
|
additionalStorage: { quantity: 2, name: 'additionalStorageGB', cost: 0.5 }
|
|
},
|
|
secretsManager: {
|
|
seats: { quantity: 3, name: 'members', cost: 13 },
|
|
additionalServiceAccounts: { quantity: 5, name: 'additionalServiceAccountsV2', cost: 1 }
|
|
},
|
|
discount: { type: 'percent-off', active: true, value: 0.25 },
|
|
cadence: 'monthly',
|
|
estimatedTax: 6.4
|
|
},
|
|
storage: {
|
|
available: 7,
|
|
readableUsed: '7 GB',
|
|
used: 0
|
|
}
|
|
}"
|
|
(callToActionClicked)="handleAction($event)"
|
|
>
|
|
</billing-subscription-card>
|
|
```
|
|
|
|
## Features
|
|
|
|
- **Dynamic Badge**: Status badge changes color and text based on subscription state
|
|
- **Contextual Callouts**: Warning, info, or danger callouts with relevant messages
|
|
- **Action Buttons**: Context-specific call-to-action buttons (update payment, contact support,
|
|
etc.)
|
|
- **Cart Summary Integration**: Embedded cart summary with pricing breakdown
|
|
- **Custom Header Support**: Cart summary can display custom headers based on subscription status
|
|
- **Date Formatting**: Consistent date formatting throughout (MMM. d, y format)
|
|
- **Computed Signals**: Efficient reactive computations using Angular signals
|
|
- **Type Safety**: Discriminated union types ensure type-safe subscription data
|
|
- **Internationalization**: All text uses i18n service for translation support
|
|
- **Event Emission**: Emits typed events for handling user actions
|
|
|
|
## Do's and Don'ts
|
|
|
|
### ✅ Do
|
|
|
|
- Handle all `callToActionClicked` events appropriately in parent components
|
|
- Provide complete `BitwardenSubscription` objects with all required fields
|
|
- Use the correct subscription status from the defined status types
|
|
- Include accurate date information for nextCharge, suspension, and cancelAt fields
|
|
- Set `showUpgradeButton` to `true` only when upgrade paths are available
|
|
- Use real translation keys that exist in the i18n messages file
|
|
- Provide accurate storage information with readable format strings
|
|
|
|
### ❌ Don't
|
|
|
|
- Omit required fields from the BitwardenSubscription type
|
|
- Use custom status strings not defined in the type
|
|
- Display upgrade buttons for users who cannot upgrade
|
|
- Ignore the `callToActionClicked` events - they require handling
|
|
- Mix subscription states (e.g., having both `canceled` date and `nextCharge`)
|
|
- Provide incorrect dates that don't match the subscription status
|
|
- Override component styles without ensuring accessibility
|
|
- Use placeholder or mock data in production environments
|
|
|
|
## Accessibility
|
|
|
|
The component includes:
|
|
|
|
- **Semantic HTML**: Proper heading hierarchy with `<h2>`, `<h3>`, `<h4>` tags
|
|
- **ARIA Labels**: Badge variants use appropriate semantic colors
|
|
- **Keyboard Navigation**: All action buttons are keyboard accessible
|
|
- **Focus Management**: Clear focus indicators on interactive elements
|
|
- **Color Contrast**: Sufficient contrast ratios for all text and badge variants
|
|
- **Screen Reader Support**: Descriptive text for all interactive elements
|
|
- **Button Types**: Proper `type="button"` attributes on all buttons
|
|
- **Date Formatting**: Human-readable date formats for assistive technologies
|