Skip to content
6 changes: 6 additions & 0 deletions .changeset/chubby-memes-pull.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
'@clerk/localizations': patch
'@clerk/clerk-js': patch
---

Added a notice in tooltip when member no has permissions to manage billing for all manager related buttons
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { useClerk } from '@clerk/shared/react';
import { useClerk, useSession } from '@clerk/shared/react';
import type { CommercePlanResource, CommerceSubscriptionPlanPeriod, PricingTableProps } from '@clerk/types';
import * as React from 'react';

import { useProtect } from '../../common';
import { usePlansContext, usePricingTableContext, useSubscriberTypeContext } from '../../contexts';
import {
Badge,
Expand All @@ -17,7 +18,7 @@ import {
Span,
Text,
} from '../../customizables';
import { Switch } from '../../elements';
import { Switch, Tooltip } from '../../elements';
import { Check, Plus } from '../../icons';
import { common, InternalThemeProvider } from '../../styledSystem';
import { colors, getClosestProfileScrollBox } from '../../utils';
Expand Down Expand Up @@ -97,13 +98,18 @@ interface CardProps {
function Card(props: CardProps) {
const { plan, planPeriod, setPlanPeriod, onSelect, props: pricingTableProps, isCompact = false } = props;
const clerk = useClerk();
const { isSignedIn } = useSession();
const { mode = 'mounted', ctaPosition: ctxCtaPosition } = usePricingTableContext();
const subscriberType = useSubscriberTypeContext();

const ctaPosition = pricingTableProps.ctaPosition || ctxCtaPosition || 'bottom';
const collapseFeatures = pricingTableProps.collapseFeatures || false;
const { id, slug } = plan;

const canManageBilling = useProtect(
has => has({ permission: 'org:sys_billing:manage' }) || subscriberType === 'user',
);

const { buttonPropsForPlan, upcomingSubscriptionsExist, activeOrUpcomingSubscription } = usePlansContext();

const showPlanDetails = (event?: React.MouseEvent<HTMLElement>) => {
Expand Down Expand Up @@ -236,15 +242,24 @@ function Card(props: CardProps) {
})}
/>
) : (
<Button
elementDescriptor={descriptors.pricingTableCardFooterButton}
block
textVariant={isCompact ? 'buttonSmall' : 'buttonLarge'}
{...buttonPropsForPlan({ plan, isCompact, selectedPlanPeriod: planPeriod })}
onClick={event => {
onSelect(plan, event);
}}
/>
<Tooltip.Root>
<Tooltip.Trigger sx={{ width: '100%' }}>
<Button
elementDescriptor={descriptors.pricingTableCardFooterButton}
block
textVariant={isCompact ? 'buttonSmall' : 'buttonLarge'}
{...buttonPropsForPlan({ plan, isCompact, selectedPlanPeriod: planPeriod })}
onClick={event => {
onSelect(plan, event);
}}
/>
</Tooltip.Trigger>
{isSignedIn && !canManageBilling && (
<Tooltip.Content
text={localizationKeys('organizationProfile.billingPage.alerts.noPermissionsToManageBilling')}
/>
)}
</Tooltip.Root>
)}
</Box>
) : (
Expand Down
4 changes: 3 additions & 1 deletion packages/clerk-js/src/ui/contexts/components/Plans.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,7 @@ export const usePlansContext = () => {
variant: 'bordered' | 'solid';
colorScheme: 'secondary' | 'primary';
isDisabled: boolean;
disabled: boolean;
} => {
const subscription = sub ?? (plan ? activeOrUpcomingSubscription(plan) : undefined);
let _selectedPlanPeriod = selectedPlanPeriod;
Expand Down Expand Up @@ -235,9 +236,10 @@ export const usePlansContext = () => {
variant: isCompact ? 'bordered' : 'solid',
colorScheme: isCompact ? 'secondary' : 'primary',
isDisabled: !canManageBilling,
disabled: !canManageBilling,
};
},
[activeOrUpcomingSubscription],
[activeOrUpcomingSubscription, canManageBilling, ctx.subscriptions],
);

const captionForSubscription = useCallback((subscription: CommerceSubscriptionResource) => {
Expand Down
46 changes: 29 additions & 17 deletions packages/clerk-js/src/ui/elements/Tooltip.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import * as React from 'react';

import { Box, descriptors, type LocalizationKey, Span, Text, useAppearance } from '../customizables';
import { usePrefersReducedMotion } from '../hooks';
import type { ThemableCssProp } from '../styledSystem';

interface TooltipOptions {
initialOpen?: boolean;
Expand Down Expand Up @@ -123,8 +124,12 @@ function Root({ children, ...options }: { children: React.ReactNode } & TooltipO
return <TooltipContext.Provider value={tooltip}>{children}</TooltipContext.Provider>;
}

const Trigger = React.forwardRef<HTMLElement, React.HTMLProps<HTMLElement>>(function TooltipTrigger(
{ children, ...props },
type TriggerProps = React.HTMLProps<HTMLElement> & {
sx?: ThemableCssProp;
};

const Trigger = React.forwardRef<HTMLElement, TriggerProps>(function TooltipTrigger(
{ children, sx, ...props },
propRef,
) {
const context = useTooltipContext();
Expand All @@ -135,7 +140,7 @@ const Trigger = React.forwardRef<HTMLElement, React.HTMLProps<HTMLElement>>(func
return null;
}

// If the child is disabled, wrap it in a span to handle hover events
// If the child is disabled, wrap it in a span to handle focus events
if (children.props.isDisabled || children.props.disabled) {
return (
<Span
Expand All @@ -144,12 +149,15 @@ const Trigger = React.forwardRef<HTMLElement, React.HTMLProps<HTMLElement>>(func
...props,
})}
data-state={context.open ? 'open' : 'closed'}
sx={{
width: 'fit-content',
display: 'inline-block',
cursor: 'not-allowed',
outline: 'none',
}}
sx={[
{
width: 'fit-content',
display: 'inline-block',
cursor: 'not-allowed',
outline: 'none',
},
sx,
]}
tabIndex={0}
>
{children}
Expand All @@ -172,8 +180,9 @@ const Content = React.forwardRef<
HTMLDivElement,
React.HTMLProps<HTMLDivElement> & {
text: string | LocalizationKey;
sx?: ThemableCssProp;
}
>(function TooltipContent({ style, text, ...props }, propRef) {
>(function TooltipContent({ style, text, sx, ...props }, propRef) {
const context = useTooltipContext();
const ref = useMergeRefs([context.refs.setFloating, propRef]);

Expand All @@ -193,13 +202,16 @@ const Content = React.forwardRef<
<Box
elementDescriptor={descriptors.tooltipContent}
style={context.transitionStyles}
sx={t => ({
paddingBlock: t.space.$1,
paddingInline: t.space.$1x5,
borderRadius: t.radii.$md,
backgroundColor: t.colors.$primary500,
maxWidth: t.sizes.$60,
})}
sx={[
t => ({
paddingBlock: t.space.$1,
paddingInline: t.space.$1x5,
borderRadius: t.radii.$md,
backgroundColor: t.colors.$primary500,
maxWidth: t.sizes.$60,
}),
sx,
]}
>
<Text
elementDescriptor={descriptors.tooltipText}
Expand Down
2 changes: 1 addition & 1 deletion packages/localizations/src/en-US.ts
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ export const enUS: LocalizationResource = {
badge__unverified: 'Unverified',
billingPage: {
alerts: {
noPermissionsToManageBilling: 'You do not have permissions to manage billing for this account.',
noPermissionsToManageBilling: 'You do not have permissions to manage billing for this organization.',
},
paymentSourcesSection: {
actionLabel__default: 'Make default',
Expand Down