forked from Talenttrust/Talenttrust-Frontend
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathReputationErrorBoundary.tsx
More file actions
142 lines (123 loc) · 4.57 KB
/
Copy pathReputationErrorBoundary.tsx
File metadata and controls
142 lines (123 loc) · 4.57 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
'use client';
import React, { Component, type ReactNode } from 'react';
import { reportError } from '@/lib/errorReporter';
// ---------------------------------------------------------------------------
// Types
// ---------------------------------------------------------------------------
export interface ReputationErrorBoundaryProps {
/** Content to protect. */
children: ReactNode;
/**
* Optional override for the fallback UI. When provided it replaces the
* built-in accessible fallback entirely — the consumer is responsible for
* rendering a retry affordance if desired.
*/
fallback?: ReactNode;
/**
* Optional callback fired after an error is caught, in addition to the
* internal `reportError` call. Useful for testing or custom instrumentation.
*/
onError?: (error: Error, info: React.ErrorInfo) => void;
}
interface State {
hasError: boolean;
error: Error | null;
}
// ---------------------------------------------------------------------------
// Component
// ---------------------------------------------------------------------------
/**
* ReputationErrorBoundary
*
* An error boundary scoped to the reputation section. When a descendant
* throws during render, the boundary:
* 1. Reports the error via `reportError` (the existing error-reporter seam).
* 2. Renders an accessible fallback UI with a visible "Retry" button.
* 3. Resets its own state on retry so the reputation section re-mounts.
*
* Accessibility:
* - The fallback container uses `role="alert"` so assistive-technology users
* are immediately informed that the section failed.
* - The "Retry" button receives focus automatically (via `autoFocus`)
* when the fallback renders, providing a clear keyboard entry point.
* - All interactive elements meet WCAG 2.4.7 focus-visible requirements.
*/
export default class ReputationErrorBoundary extends Component<
ReputationErrorBoundaryProps,
State
> {
state: State = { hasError: false, error: null };
// ------------------------------------------------------------------
// Lifecycle
// ------------------------------------------------------------------
static getDerivedStateFromError(error: Error): State {
return { hasError: true, error };
}
componentDidCatch(error: Error, info: React.ErrorInfo): void {
reportError(error, 'ReputationErrorBoundary', 'error', {
componentStack: info.componentStack ?? undefined,
});
this.props.onError?.(error, info);
}
// ------------------------------------------------------------------
// Handlers
// ------------------------------------------------------------------
/** Clears the error state so the children are re-mounted on the next render. */
handleRetry = (): void => {
this.setState({ hasError: false, error: null });
};
// ------------------------------------------------------------------
// Render
// ------------------------------------------------------------------
render(): ReactNode {
const { hasError, error } = this.state;
const { children, fallback } = this.props;
if (!hasError) {
return children;
}
// Custom fallback supplied by the consumer takes full precedence.
if (fallback !== undefined) {
return fallback;
}
// ----------------------------------------------------------------
// Built-in accessible fallback
// ----------------------------------------------------------------
return (
<div
role="alert"
aria-live="assertive"
aria-atomic="true"
className="rounded-2xl border border-red-200 bg-red-50 p-6 text-center shadow-sm"
>
<p className="text-base font-semibold text-red-800">
The reputation section couldn’t load.
</p>
{error?.message && (
<p
className="mt-1 text-sm text-red-600"
data-testid="reputation-error-message"
>
{error.message}
</p>
)}
<p className="mt-2 text-sm text-red-700">
This is likely a temporary issue. Use the button below to try again.
</p>
<button
type="button"
autoFocus
onClick={this.handleRetry}
className={[
'mt-4 inline-flex items-center gap-2 rounded-xl',
'bg-red-700 px-4 py-2 text-sm font-semibold text-white',
'transition hover:bg-red-800',
'focus-visible:outline focus-visible:outline-4',
'focus-visible:outline-offset-2 focus-visible:outline-red-600',
].join(' ')}
>
Retry
</button>
</div>
);
}
}