import { AriaAttributes, ReactNode, useId } from 'react'; import { clsx } from 'clsx'; import StatusIcon from '../../statusIcon'; import Body from '../../body'; import Button from '../../button'; import { Breakpoint, Typography } from '../../common'; import AvatarView, { AvatarViewProps } from '../../avatarView'; import Image from '../../image'; import { ButtonProps } from '../../button/Button.types'; import { PrimitivePrompt, PrimitivePromptProps } from '../PrimitivePrompt'; import { BadgeAssetsProps } from '../../badge'; import { GiftBox } from '@transferwise/icons'; import { useScreenSize } from '../../common/hooks/useScreenSize'; export type ActionPromptProps = { title: ReactNode; description?: ReactNode; /** @default {} */ media?: { imgSrc?: string; avatar?: Pick & { asset?: AvatarViewProps['children']; badge?: Pick; }; 'aria-label'?: string; 'aria-hidden'?: boolean; }; action: Pick & { label: ButtonProps['children']; }; actionSecondary?: Pick & { label: ButtonProps['children']; }; 'aria-label'?: AriaAttributes['aria-label']; } & Pick & { /** * The sentiment determines the colour scheme * @default 'neutral' */ sentiment?: PrimitivePromptProps['sentiment']; }; /** * Use an action prompt for optional feedback that doesn't require immediate action, such as feature upsells, warnings, or suggestions. These prompts are typically used outside of core product flows (e.g., Launchpad, Recipient, or Transaction screens) and can be addressed at the user's convenience. * * If your message is about immediate user feedback (e.g., form submission errors, download failures, missing data warnings), use an [info prompt](https://storybook.wise.design/?path=/docs/prompts-infoprompt--docs) instead. * * Guidance can be found in the [design system](https://wise.design/components/action-prompt). */ export const ActionPrompt = ({ sentiment = 'neutral', title, description, onDismiss, media = {}, action, actionSecondary, id, className, 'data-testid': testId, 'aria-label': ariaLabel, }: ActionPromptProps) => { const isMobile = !useScreenSize(Breakpoint.MEDIUM); const mediaId = useId(); const titleId = useId(); const descId = useId(); const ariaLabelledByIds = [ media['aria-hidden'] ? undefined : mediaId, ariaLabel ? undefined : titleId, ] .filter(Boolean) .join(' '); const renderMedia = () => { if (media?.imgSrc) { return ( {media['aria-label'] ); } if (media?.avatar) { const badge = media.avatar.badge ? media.avatar.badge : sentiment === 'proposition' ? {} : { status: sentiment }; return ( {media.avatar.asset} ); } return sentiment === 'proposition' ? ( ) : ( ); }; return ( {actionSecondary && ( // @ts-expect-error onClick type mismatch )} {/* @ts-expect-error onClick type mismatch */} } role="region" onDismiss={onDismiss} {...(ariaLabel ? { 'aria-label': ariaLabel } : { 'aria-labelledby': ariaLabelledByIds, 'aria-describedby': descId, })} >
{title} {description && ( {description} )}
); }; export default ActionPrompt;