-
- {stat?.value}
-
-
- {stat?.label}
+ const isCopied = copiedIndex === index;
+ return (
+
+
+
+
+ {stat?.value}
+
+
+
+
+ {stat?.label}
+
+
-
-
- );
-}
+ );
+ }
diff --git a/components/error-boundary.tsx b/components/error-boundary.tsx
index ab05342..5ae7045 100644
--- a/components/error-boundary.tsx
+++ b/components/error-boundary.tsx
@@ -3,9 +3,15 @@
import React, { Component, ErrorInfo, ReactNode } from "react";
import { ErrorRecoveryScreen } from "@/components/error/ErrorRecoveryScreen";
+interface FallbackRenderProps {
+ error: Error;
+ incidentId: string | null;
+ resetErrorBoundary: () => void;
+}
+
interface Props {
children: ReactNode;
- fallback?: ReactNode;
+ fallback?: ReactNode | ((props: FallbackRenderProps) => ReactNode);
}
interface State {
@@ -50,6 +56,13 @@ export class ErrorBoundary extends Component
{
render() {
if (this.state.hasError) {
if (this.props.fallback) {
+ if (typeof this.props.fallback === "function") {
+ return (this.props.fallback as (props: FallbackRenderProps) => ReactNode)({
+ error: this.state.error!,
+ incidentId: this.state.incidentId,
+ resetErrorBoundary: this.handleReset,
+ });
+ }
return this.props.fallback;
}
diff --git a/src/components/BetFormErrorFallback.tsx b/src/components/BetFormErrorFallback.tsx
new file mode 100644
index 0000000..1205d6b
--- /dev/null
+++ b/src/components/BetFormErrorFallback.tsx
@@ -0,0 +1,66 @@
+"use client";
+
+import React from "react";
+import { Button } from "@/components/ui/button";
+import { AlertTriangle, RefreshCw } from "lucide-react";
+
+interface BetFormErrorFallbackProps {
+ error: Error;
+ incidentId: string | null;
+ resetErrorBoundary: () => void;
+}
+
+/**
+ * BetForm error-boundary fallback UI.
+ *
+ * Renders an attractive, card-styled fallback with a retry action when BetForm
+ * throws during render. Respects design tokens and dark mode, and exposes the
+ * incident id + error message for transparency without leaking stack traces.
+ */
+export function BetFormErrorFallback({
+ error,
+ incidentId,
+ resetErrorBoundary,
+}: BetFormErrorFallbackProps) {
+ return (
+
+
+
+
+
+
+ We couldn't load the bet form
+
+
+ An unexpected error occurred while rendering the bet form. Please try again.
+
+
+ {incidentId && (
+
+ Incident ID: {incidentId}
+
+ )}
+
+
+
+
+
+ {error?.message && (
+
+ {error.message}
+
+ )}
+
+
+
+ );
+}
\ No newline at end of file
diff --git a/src/pages/BetForm.error-boundary.test.tsx b/src/pages/BetForm.error-boundary.test.tsx
new file mode 100644
index 0000000..8f80933
--- /dev/null
+++ b/src/pages/BetForm.error-boundary.test.tsx
@@ -0,0 +1,50 @@
+import React from "react";
+import { render, screen, fireEvent } from "@testing-library/react";
+import BetForm from "../BetForm";
+import { BetFormErrorFallback } from "../components/BetFormErrorFallback";
+
+describe("BetForm error boundary fallback (#773)", () => {
+ it("renders the custom fallback when a child throws", () => {
+ const spy = jest.spyOn(console, "error").mockImplementation(() => {});
+ try {
+ render(
+ {}}
+ />
+ );
+ expect(screen.getByText(/couldn't load the bet form/i)).toBeInTheDocument();
+ expect(screen.getByRole("button", { name: /Retry/i })).toBeInTheDocument();
+ expect(screen.getByText(/Incident ID: test-123/i)).toBeInTheDocument();
+ expect(screen.getByTestId("bet-form-error-msg")).toHaveTextContent("boom");
+ } finally {
+ spy.mockRestore();
+ }
+ });
+
+ it("calls resetErrorBoundary when Retry is clicked", () => {
+ const reset = jest.fn();
+ render(
+
+ );
+ fireEvent.click(screen.getByRole("button", { name: /Retry/i }));
+ expect(reset).toHaveBeenCalledTimes(1);
+ });
+
+ it("renders without incidentId when none is provided", () => {
+ render(
+
+ );
+ expect(screen.getByRole("button", { name: /Retry/i })).toBeInTheDocument();
+ expect(screen.queryByText(/Incident ID:/i)).not.toBeInTheDocument();
+ });
+});
\ No newline at end of file
diff --git a/src/pages/BetForm.tsx b/src/pages/BetForm.tsx
index 313897e..b22f3f2 100644
--- a/src/pages/BetForm.tsx
+++ b/src/pages/BetForm.tsx
@@ -4,6 +4,8 @@ import { Card, CardContent } from "@/components/ui/card";
import { Button } from "@/components/ui/button";
import { Loader2 } from "lucide-react";
import { StellarWaveEmptyState } from "../components/EmptyState";
+import { ErrorBoundary } from "../../components/error-boundary";
+import { BetFormErrorFallback } from "../components/BetFormErrorFallback";
type FormState = "idle" | "submitting" | "success" | "error";
@@ -41,7 +43,7 @@ export default function BetForm({ campaignActive = true }: BetFormProps = {}) {
{!campaignActive ? (
) : (
- <>
+
@@ -95,7 +97,7 @@ export default function BetForm({ campaignActive = true }: BetFormProps = {}) {
)}
- >
+
)}