|
1 | | -importMuiAlert,{ |
2 | | -typeAlertColorasMuiAlertColor, |
3 | | -typeAlertPropsasMuiAlertProps, |
4 | | -}from"@mui/material/Alert"; |
5 | | -importCollapsefrom"@mui/material/Collapse"; |
| 1 | +import{cva}from"class-variance-authority"; |
6 | 2 | import{Button}from"components/Button/Button"; |
| 3 | +import{ |
| 4 | +CircleAlertIcon, |
| 5 | +CircleCheckIcon, |
| 6 | +InfoIcon, |
| 7 | +TriangleAlertIcon, |
| 8 | +XIcon, |
| 9 | +}from"lucide-react"; |
7 | 10 | import{ |
8 | 11 | typeFC, |
| 12 | +forwardRef, |
9 | 13 | typePropsWithChildren, |
10 | 14 | typeReactNode, |
11 | 15 | useState, |
12 | 16 | }from"react"; |
13 | | -exporttypeAlertColor=MuiAlertColor; |
| 17 | +import{cn}from"utils/cn"; |
| 18 | + |
| 19 | +constalertVariants=cva( |
| 20 | +"relative w-full rounded-lg border border-solid p-4 text-left", |
| 21 | +{ |
| 22 | +variants:{ |
| 23 | +severity:{ |
| 24 | +info:"", |
| 25 | +success:"", |
| 26 | +warning:"", |
| 27 | +error:"", |
| 28 | +}, |
| 29 | +prominent:{ |
| 30 | +true:"", |
| 31 | +false:"", |
| 32 | +}, |
| 33 | +}, |
| 34 | +compoundVariants:[ |
| 35 | +{ |
| 36 | +prominent:false, |
| 37 | +className:"border-border-default bg-surface-secondary", |
| 38 | +}, |
| 39 | +{ |
| 40 | +severity:"success", |
| 41 | +prominent:true, |
| 42 | +className:"border-border-success bg-surface-green", |
| 43 | +}, |
| 44 | +{ |
| 45 | +severity:"warning", |
| 46 | +prominent:true, |
| 47 | +className:"border-border-warning bg-surface-orange", |
| 48 | +}, |
| 49 | +{ |
| 50 | +severity:"error", |
| 51 | +prominent:true, |
| 52 | +className:"border-border-destructive bg-surface-red", |
| 53 | +}, |
| 54 | +], |
| 55 | +defaultVariants:{ |
| 56 | +severity:"info", |
| 57 | +prominent:false, |
| 58 | +}, |
| 59 | +}, |
| 60 | +); |
| 61 | + |
| 62 | +constseverityIcons={ |
| 63 | +info:{icon:InfoIcon,className:"text-highlight-sky"}, |
| 64 | +success:{icon:CircleCheckIcon,className:"text-content-success"}, |
| 65 | +warning:{icon:TriangleAlertIcon,className:"text-content-warning"}, |
| 66 | +error:{icon:CircleAlertIcon,className:"text-content-destructive"}, |
| 67 | +}asconst; |
| 68 | + |
| 69 | +exporttypeAlertColor="info"|"success"|"warning"|"error"; |
14 | 70 |
|
15 | | -exporttypeAlertProps=MuiAlertProps&{ |
| 71 | +exporttypeAlertProps={ |
16 | 72 | actions?:ReactNode; |
17 | 73 | dismissible?:boolean; |
18 | 74 | onDismiss?:()=>void; |
| 75 | +severity?:AlertColor; |
| 76 | +prominent?:boolean; |
| 77 | +children?:ReactNode; |
| 78 | +className?:string; |
19 | 79 | }; |
20 | 80 |
|
21 | 81 | exportconstAlert:FC<AlertProps>=({ |
22 | 82 | children, |
23 | 83 | actions, |
24 | 84 | dismissible, |
25 | 85 | severity="info", |
| 86 | +prominent=false, |
26 | 87 | onDismiss, |
27 | | -...alertProps |
| 88 | +className, |
| 89 | +...props |
28 | 90 | })=>{ |
29 | 91 | const[open,setOpen]=useState(true); |
30 | 92 |
|
31 | | -// Can't only rely on MUI's hiding behavior inside flex layouts, because even |
32 | | -// though MUI will make a dismissed alert have zero height, the alert will |
33 | | -// still behave as a flex child and introduce extra row/column gaps |
34 | 93 | if(!open){ |
35 | 94 | returnnull; |
36 | 95 | } |
37 | 96 |
|
| 97 | +const{icon:Icon,className:iconClassName}=severityIcons[severity]; |
| 98 | + |
38 | 99 | return( |
39 | | -<Collapsein> |
40 | | -<MuiAlert |
41 | | -{...alertProps} |
42 | | -css={{textAlign:"left"}} |
43 | | -severity={severity} |
44 | | -action={ |
45 | | -<> |
46 | | -{/* CTAs passed in by the consumer */} |
47 | | -{actions} |
| 100 | +<div |
| 101 | +role="alert" |
| 102 | +className={cn(alertVariants({ severity, prominent}),className)} |
| 103 | +{...props} |
| 104 | +> |
| 105 | +<divclassName="flex items-center justify-between gap-4 text-sm"> |
| 106 | +<divclassName="flex flex-row items-start gap-3"> |
| 107 | +<IconclassName={cn("size-icon-sm mt-[3px]",iconClassName)}/> |
| 108 | +<divclassName="flex-1">{children}</div> |
| 109 | +</div> |
| 110 | +<divclassName="flex items-center gap-2"> |
| 111 | +{actions} |
48 | 112 |
|
49 | | -{/* close CTA */} |
50 | | -{dismissible&&( |
51 | | -<Button |
52 | | -variant="subtle" |
53 | | -size="sm" |
54 | | -onClick={()=>{ |
55 | | -setOpen(false); |
56 | | -onDismiss?.(); |
57 | | -}} |
58 | | -data-testid="dismiss-banner-btn" |
59 | | -> |
60 | | -Dismiss |
61 | | -</Button> |
62 | | -)} |
63 | | -</> |
64 | | -} |
65 | | -> |
66 | | -{children} |
67 | | -</MuiAlert> |
68 | | -</Collapse> |
| 113 | +{dismissible&&( |
| 114 | +<Button |
| 115 | +variant="subtle" |
| 116 | +size="icon" |
| 117 | +className="!size-auto !min-w-0 !p-0" |
| 118 | +onClick={()=>{ |
| 119 | +setOpen(false); |
| 120 | +onDismiss?.(); |
| 121 | +}} |
| 122 | +data-testid="dismiss-banner-btn" |
| 123 | +aria-label="Dismiss" |
| 124 | +> |
| 125 | +<XIconclassName="!size-icon-sm !p-0"/> |
| 126 | +</Button> |
| 127 | +)} |
| 128 | +</div> |
| 129 | +</div> |
| 130 | +</div> |
69 | 131 | ); |
70 | 132 | }; |
71 | 133 |
|
72 | 134 | exportconstAlertDetail:FC<PropsWithChildren>=({ children})=>{ |
73 | 135 | return( |
74 | | -<span |
75 | | -css={(theme)=>({color:theme.palette.text.secondary,fontSize:13})} |
76 | | -data-chromatic="ignore" |
77 | | -> |
| 136 | +<spanclassName="m-0 text-sm"data-chromatic="ignore"> |
78 | 137 | {children} |
79 | 138 | </span> |
80 | 139 | ); |
81 | 140 | }; |
| 141 | + |
| 142 | +exportconstAlertTitle=forwardRef< |
| 143 | +HTMLHeadingElement, |
| 144 | +React.HTMLAttributes<HTMLHeadingElement> |
| 145 | +>(({ className, ...props},ref)=>( |
| 146 | +<h1 |
| 147 | +ref={ref} |
| 148 | +className={cn("m-0 mb-1 text-sm font-medium",className)} |
| 149 | +{...props} |
| 150 | +/> |
| 151 | +)); |