@@ -8,16 +8,37 @@ interface ErrorFallbackProps {
88 resetError : ( ) => void ;
99 onReload ?: ( ) => void ;
1010 onGoHome ?: ( ) => void ;
11+ /** When true, show a sanitized one-line detail (dev only by default). */
12+ showErrorDetail ?: boolean ;
13+ }
14+
15+ function isSafeUserFacingDetail ( message : string ) : boolean {
16+ const trimmed = message . trim ( ) ;
17+ if ( ! trimmed || trimmed . length > 160 ) return false ;
18+ // Block stack-like / path / secret-looking content
19+ if ( / [ / \\ ] | \. t s x ? \b | \. j s x ? \b | a t \s + \S + | E r r o r : | T y p e E r r o r | R e f e r e n c e E r r o r | h t t p / i. test ( trimmed ) ) {
20+ return false ;
21+ }
22+ return true ;
1123}
1224
1325const ErrorFallback : React . FC < ErrorFallbackProps > = ( {
1426 error,
27+ resetError,
1528 onReload = reloadPage ,
1629 onGoHome = goHome ,
30+ showErrorDetail = import . meta. env . DEV ,
1731} ) => {
1832 const { t } = useTranslation ( ) ;
33+ const detail =
34+ showErrorDetail && error ?. message && isSafeUserFacingDetail ( error . message )
35+ ? error . message
36+ : null ;
37+
1938 return (
2039 < div
40+ role = "alert"
41+ aria-live = "assertive"
2142 style = { {
2243 display : "flex" ,
2344 alignItems : "center" ,
@@ -51,7 +72,7 @@ const ErrorFallback: React.FC<ErrorFallbackProps> = ({
5172 marginBottom : "8px" ,
5273 } }
5374 >
54- < AlertOctagon size = { 48 } />
75+ < AlertOctagon size = { 48 } aria-hidden = "true" />
5576 </ div >
5677
5778 < div >
@@ -71,8 +92,9 @@ const ErrorFallback: React.FC<ErrorFallbackProps> = ({
7192 >
7293 { t ( "errorFallback.message" ) }
7394 </ p >
74- { error ?. message && (
95+ { detail && (
7596 < div
97+ data-testid = "error-fallback-detail"
7698 style = { {
7799 background : "rgba(0,0,0,0.2)" ,
78100 border : "1px solid var(--border-glass)" ,
@@ -87,7 +109,7 @@ const ErrorFallback: React.FC<ErrorFallbackProps> = ({
87109 fontFamily : "monospace" ,
88110 } }
89111 >
90- { error . message }
112+ { detail }
91113 </ div >
92114 ) }
93115 </ div >
@@ -102,20 +124,32 @@ const ErrorFallback: React.FC<ErrorFallbackProps> = ({
102124 } }
103125 >
104126 < button
127+ type = "button"
105128 className = "btn btn-primary"
129+ onClick = { resetError }
130+ style = { { width : "100%" , padding : "14px" } }
131+ >
132+ < RefreshCw size = { 18 } aria-hidden = "true" />
133+ { t ( "errorFallback.tryAgain" ) }
134+ </ button >
135+
136+ < button
137+ type = "button"
138+ className = "btn btn-outline"
106139 onClick = { onReload }
107140 style = { { width : "100%" , padding : "14px" } }
108141 >
109- < RefreshCw size = { 18 } />
142+ < RefreshCw size = { 18 } aria-hidden = "true" />
110143 { t ( "errorFallback.reload" ) }
111144 </ button >
112145
113146 < button
147+ type = "button"
114148 className = "btn btn-outline"
115149 onClick = { onGoHome }
116150 style = { { width : "100%" , padding : "14px" } }
117151 >
118- < Home size = { 18 } />
152+ < Home size = { 18 } aria-hidden = "true" />
119153 { t ( "errorFallback.goHome" ) }
120154 </ button >
121155 </ div >
0 commit comments