Skip to content

Commit 6ec374c

Browse files
feat(clerk-js,types): Add PaymentSourceRow for displaying payment sources (#5570)
Co-authored-by: Alex Carpenter <alex.carpenter@clerk.dev> Co-authored-by: Alex Carpenter <im.alexcarpenter@gmail.com>
1 parent ae2d1a9 commit 6ec374c

9 files changed

Lines changed: 89 additions & 53 deletions

File tree

.changeset/tall-clocks-call.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@clerk/clerk-js': patch
3+
---
4+
5+
Add `PaymentSourceRow` for displaying payment sources

packages/clerk-js/bundlewatch.config.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
{ "path": "./dist/keylessPrompt*.js", "maxSize": "5.9KB" },
2222
{ "path": "./dist/pricingTable*.js", "maxSize": "5KB" },
2323
{ "path": "./dist/checkout*.js", "maxSize": "3KB" },
24-
{ "path": "./dist/paymentSources*.js", "maxSize": "8.1KB" },
24+
{ "path": "./dist/paymentSources*.js", "maxSize": "8.2KB" },
2525
{ "path": "./dist/up-billing-page*.js", "maxSize": "1KB" },
2626
{ "path": "./dist/op-billing-page*.js", "maxSize": "1KB" },
2727
{ "path": "./dist/sessionTasks*.js", "maxSize": "1KB" }

packages/clerk-js/src/ui/components/Checkout/CheckoutForm.tsx

Lines changed: 9 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,13 @@ import type {
99
import { useMemo, useState } from 'react';
1010

1111
import { __experimental_PaymentSourcesContext, useCheckoutContext } from '../../contexts';
12-
import { Box, Button, Col, descriptors, Flex, Form, Icon, localizationKeys, Text } from '../../customizables';
12+
import { Box, Button, Col, descriptors, Form, localizationKeys } from '../../customizables';
1313
import { Alert, Disclosure, Divider, Drawer, LineItems, Select, SelectButton, SelectOptionList } from '../../elements';
1414
import { useFetch } from '../../hooks';
15-
import { ArrowUpDown, CreditCard } from '../../icons';
15+
import { ArrowUpDown } from '../../icons';
1616
import { animations } from '../../styledSystem';
1717
import { handleError } from '../../utils';
18-
import { AddPaymentSource } from '../PaymentSources';
18+
import { AddPaymentSource, PaymentSourceRow } from '../PaymentSources';
1919

2020
const capitalize = (name: string) => name[0].toUpperCase() + name.slice(1);
2121

@@ -170,6 +170,7 @@ const CheckoutFormElements = ({
170170
<Disclosure.Content>
171171
<Col gap={3}>
172172
<PaymentSourceMethods
173+
checkout={checkout}
173174
paymentSources={paymentSources}
174175
totalDueNow={checkout.totals.totalDueNow || checkout.totals.grandTotal}
175176
onPaymentSourceSubmit={onPaymentSourceSubmit}
@@ -208,19 +209,21 @@ const CheckoutFormElements = ({
208209
};
209210

210211
const PaymentSourceMethods = ({
212+
checkout,
211213
totalDueNow,
212214
paymentSources,
213215
onPaymentSourceSubmit,
214216
isSubmitting,
215217
}: {
218+
checkout: __experimental_CommerceCheckoutResource;
216219
totalDueNow: __experimental_CommerceMoney;
217220
paymentSources: __experimental_CommercePaymentSourceResource[];
218221
onPaymentSourceSubmit: React.FormEventHandler<HTMLFormElement>;
219222
isSubmitting: boolean;
220223
}) => {
221224
const [selectedPaymentSource, setSelectedPaymentSource] = useState<
222225
__experimental_CommercePaymentSourceResource | undefined
223-
>(paymentSources.length > 0 ? paymentSources[0] : undefined);
226+
>(checkout.paymentSource || paymentSources.find(p => p.isDefault));
224227

225228
const options = useMemo(() => {
226229
return paymentSources.map(source => {
@@ -241,7 +244,7 @@ const PaymentSourceMethods = ({
241244
})}
242245
>
243246
<Select
244-
elementId='role'
247+
elementId='paymentSource'
245248
options={options}
246249
value={selectedPaymentSource?.id || null}
247250
onChange={option => {
@@ -263,20 +266,7 @@ const PaymentSourceMethods = ({
263266
backgroundColor: t.colors.$colorBackground,
264267
})}
265268
>
266-
{selectedPaymentSource && (
267-
<Flex
268-
gap={3}
269-
align='center'
270-
>
271-
<Icon icon={CreditCard} />
272-
<Text
273-
as='span'
274-
colorScheme='body'
275-
>
276-
{capitalize(selectedPaymentSource.cardType)}{selectedPaymentSource.last4}
277-
</Text>
278-
</Flex>
279-
)}
269+
{selectedPaymentSource && <PaymentSourceRow paymentSource={selectedPaymentSource} />}
280270
</SelectButton>
281271
<SelectOptionList
282272
sx={t => ({
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
import type { __experimental_CommercePaymentSourceResource } from '@clerk/types';
2+
3+
import { Badge, descriptors, Flex, Icon, localizationKeys, Text } from '../../customizables';
4+
import { ApplePay, CreditCard } from '../../icons';
5+
6+
export const PaymentSourceRow = ({
7+
paymentSource,
8+
}: {
9+
paymentSource: __experimental_CommercePaymentSourceResource;
10+
}) => {
11+
return (
12+
<Flex
13+
sx={{ overflow: 'hidden' }}
14+
gap={2}
15+
align='baseline'
16+
elementDescriptor={descriptors.paymentSourceRow}
17+
>
18+
<Icon
19+
icon={paymentSource.walletType === 'apple_pay' ? ApplePay : CreditCard}
20+
sx={{ alignSelf: 'center' }}
21+
elementDescriptor={descriptors.paymentSourceRowIcon}
22+
/>
23+
<Text
24+
sx={t => ({ color: t.colors.$colorText, textTransform: 'capitalize' })}
25+
truncate
26+
elementDescriptor={descriptors.paymentSourceRowType}
27+
>
28+
{paymentSource.paymentMethod === 'card' ? paymentSource.cardType : paymentSource.paymentMethod}
29+
</Text>
30+
<Text
31+
sx={t => ({ color: t.colors.$colorTextSecondary })}
32+
variant='caption'
33+
truncate
34+
elementDescriptor={descriptors.paymentSourceRowValue}
35+
>
36+
{paymentSource.paymentMethod === 'card' ? `⋯ ${paymentSource.last4}` : '-'}
37+
</Text>
38+
{paymentSource.isDefault && (
39+
<Badge
40+
elementDescriptor={descriptors.paymentSourceRowBadge}
41+
elementId={descriptors.paymentSourceRowBadge.setId('default')}
42+
localizationKey={localizationKeys('badge__default')}
43+
/>
44+
)}
45+
{paymentSource.status === 'expired' && (
46+
<Badge
47+
elementDescriptor={descriptors.paymentSourceRowBadge}
48+
elementId={descriptors.paymentSourceRowBadge.setId('expired')}
49+
colorScheme='danger'
50+
localizationKey={localizationKeys('badge__expired')}
51+
/>
52+
)}
53+
</Flex>
54+
);
55+
};

packages/clerk-js/src/ui/components/PaymentSources/PaymentSources.tsx

Lines changed: 3 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,14 @@ import { Fragment, useRef } from 'react';
44

55
import { RemoveResourceForm } from '../../common';
66
import { usePaymentSourcesContext } from '../../contexts';
7-
import { Badge, Flex, Icon, localizationKeys, Text } from '../../customizables';
7+
import { localizationKeys } from '../../customizables';
88
import { ProfileSection, ThreeDotsMenu, useCardState } from '../../elements';
99
import { Action } from '../../elements/Action';
1010
import { useActionContext } from '../../elements/Action/ActionRoot';
1111
import { useFetch } from '../../hooks';
12-
import { ApplePay, CreditCard } from '../../icons';
1312
import { handleError } from '../../utils';
1413
import { AddPaymentSource } from './AddPaymentSource';
14+
import { PaymentSourceRow } from './PaymentSourceRow';
1515

1616
const AddScreen = ({ onSuccess }: { onSuccess: () => void }) => {
1717
const { close } = useActionContext();
@@ -108,36 +108,7 @@ const PaymentSources = (_: __experimental_PaymentSourcesProps) => {
108108
{paymentSources.map(paymentSource => (
109109
<Fragment key={paymentSource.id}>
110110
<ProfileSection.Item id='paymentSources'>
111-
<Flex
112-
sx={{ overflow: 'hidden' }}
113-
gap={2}
114-
align='baseline'
115-
>
116-
<Icon
117-
icon={paymentSource.walletType === 'apple_pay' ? ApplePay : CreditCard}
118-
sx={{ alignSelf: 'center' }}
119-
/>
120-
<Text
121-
sx={t => ({ color: t.colors.$colorText, textTransform: 'capitalize' })}
122-
truncate
123-
>
124-
{paymentSource.paymentMethod === 'card' ? paymentSource.cardType : paymentSource.paymentMethod}
125-
</Text>
126-
<Text
127-
sx={t => ({ color: t.colors.$colorTextSecondary })}
128-
variant='caption'
129-
truncate
130-
>
131-
{paymentSource.paymentMethod === 'card' ? `⋯ ${paymentSource.last4}` : '-'}
132-
</Text>
133-
{paymentSource.isDefault && <Badge localizationKey={localizationKeys('badge__default')} />}
134-
{paymentSource.status === 'expired' && (
135-
<Badge
136-
colorScheme='danger'
137-
localizationKey={localizationKeys('badge__expired')}
138-
/>
139-
)}
140-
</Flex>
111+
<PaymentSourceRow paymentSource={paymentSource} />
141112
<PaymentSourceMenu
142113
paymentSource={paymentSource}
143114
revalidate={revalidate}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
export * from './AddPaymentSource';
22
export * from './PaymentSources';
3+
export * from './PaymentSourceRow';

packages/clerk-js/src/ui/customizables/elementDescriptors.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -330,6 +330,13 @@ export const APPEARANCE_KEYS = containsAllElementsConfigKeys([
330330
'selectOptionsContainer',
331331
'selectOption',
332332

333+
'paymentSourceRow',
334+
'paymentSourceRowIcon',
335+
'paymentSourceRowText',
336+
'paymentSourceRowType',
337+
'paymentSourceRowValue',
338+
'paymentSourceRowBadge',
339+
333340
'menuButton',
334341
'menuButtonEllipsis',
335342
'menuList',

packages/types/src/appearance.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -454,6 +454,13 @@ export type ElementsConfig = {
454454
selectOptionsContainer: WithOptions<SelectId>;
455455
selectOption: WithOptions<SelectId>;
456456

457+
paymentSourceRow: WithOptions;
458+
paymentSourceRowIcon: WithOptions;
459+
paymentSourceRowText: WithOptions;
460+
paymentSourceRowType: WithOptions;
461+
paymentSourceRowValue: WithOptions;
462+
paymentSourceRowBadge: WithOptions<'default' | 'expired'>;
463+
457464
menuButton: WithOptions<MenuId>;
458465
menuButtonEllipsis: WithOptions;
459466
menuList: WithOptions<MenuId>;

packages/types/src/elementIds.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,4 +52,4 @@ export type OrganizationPreviewId =
5252
export type CardActionId = 'havingTrouble' | 'alternativeMethods' | 'signUp' | 'signIn' | 'usePasskey' | 'waitlist';
5353

5454
export type MenuId = 'invitation' | 'member' | ProfileSectionId;
55-
export type SelectId = 'countryCode' | 'role';
55+
export type SelectId = 'countryCode' | 'role' | 'paymentSource';

0 commit comments

Comments
 (0)