Skip to content

Commit fda1aee

Browse files
Lms24HHK1
andauthored
feat(v8/react): Add a handled prop to ErrorBoundary (#14978)
backport of #14560 --------- Co-authored-by: Henry Huck <henryhuck@hotmail.fr>
1 parent 5182853 commit fda1aee

File tree

3 files changed

+51
-42
lines changed

3 files changed

+51
-42
lines changed

CHANGELOG.md

+2
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010

1111
- "You miss 100 percent of the chances you don't take. — Wayne Gretzky" — Michael Scott
1212

13+
Work in this release was contributed by @HHK1. Thank you for your contribution!
14+
1315
## 8.48.0
1416

1517
### Deprecations

packages/react/src/errorboundary.tsx

+8-1
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,12 @@ export type ErrorBoundaryProps = {
3535
*
3636
*/
3737
fallback?: React.ReactElement | FallbackRender | undefined;
38+
/**
39+
* If set to `true` or `false`, the error `handled` property will be set to the given value.
40+
* If unset, the default behaviour is to rely on the presence of the `fallback` prop to determine
41+
* if the error was handled or not.
42+
*/
43+
handled?: boolean | undefined;
3844
/** Called when the error boundary encounters an error */
3945
onError?: ((error: unknown, componentStack: string | undefined, eventId: string) => void) | undefined;
4046
/** Called on componentDidMount() */
@@ -107,7 +113,8 @@ class ErrorBoundary extends React.Component<ErrorBoundaryProps, ErrorBoundarySta
107113
beforeCapture(scope, error, passedInComponentStack);
108114
}
109115

110-
const eventId = captureReactException(error, errorInfo, { mechanism: { handled: !!this.props.fallback } });
116+
const handled = this.props.handled != null ? this.props.handled : !!this.props.fallback;
117+
const eventId = captureReactException(error, errorInfo, { mechanism: { handled } });
111118

112119
if (onError) {
113120
onError(error, passedInComponentStack, eventId);

packages/react/test/errorboundary.test.tsx

+41-41
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { fireEvent, render, screen } from '@testing-library/react';
44
import * as React from 'react';
55
import { useState } from 'react';
66

7-
import type { ErrorBoundaryProps } from '../src/errorboundary';
7+
import type { ErrorBoundaryProps, FallbackRender } from '../src/errorboundary';
88
import { ErrorBoundary, UNKNOWN_COMPONENT, withErrorBoundary } from '../src/errorboundary';
99

1010
const mockCaptureException = jest.fn();
@@ -537,47 +537,47 @@ describe('ErrorBoundary', () => {
537537
expect(mockOnReset).toHaveBeenCalledTimes(1);
538538
expect(mockOnReset).toHaveBeenCalledWith(expect.any(Error), expect.any(String), expect.any(String));
539539
});
540+
it.each`
541+
fallback | handled | expected
542+
${true} | ${undefined} | ${true}
543+
${false} | ${undefined} | ${false}
544+
${true} | ${false} | ${false}
545+
${true} | ${true} | ${true}
546+
${false} | ${true} | ${true}
547+
${false} | ${false} | ${false}
548+
`(
549+
'sets `handled: $expected` when `handled` is $handled and `fallback` is $fallback',
550+
async ({
551+
fallback,
552+
handled,
553+
expected,
554+
}: {
555+
fallback: boolean;
556+
handled: boolean | undefined;
557+
expected: boolean;
558+
}) => {
559+
const fallbackComponent: FallbackRender | undefined = fallback
560+
? ({ resetError }) => <button data-testid="reset" onClick={resetError} />
561+
: undefined;
562+
render(
563+
<TestApp handled={handled} fallback={fallbackComponent}>
564+
<h1>children</h1>
565+
</TestApp>,
566+
);
540567

541-
it('sets `handled: true` when a fallback is provided', async () => {
542-
render(
543-
<TestApp fallback={({ resetError }) => <button data-testid="reset" onClick={resetError} />}>
544-
<h1>children</h1>
545-
</TestApp>,
546-
);
547-
548-
expect(mockCaptureException).toHaveBeenCalledTimes(0);
549-
550-
const btn = screen.getByTestId('errorBtn');
551-
fireEvent.click(btn);
552-
553-
expect(mockCaptureException).toHaveBeenCalledTimes(1);
554-
expect(mockCaptureException).toHaveBeenLastCalledWith(expect.any(Object), {
555-
captureContext: {
556-
contexts: { react: { componentStack: expect.any(String) } },
557-
},
558-
mechanism: { handled: true },
559-
});
560-
});
561-
562-
it('sets `handled: false` when no fallback is provided', async () => {
563-
render(
564-
<TestApp>
565-
<h1>children</h1>
566-
</TestApp>,
567-
);
568-
569-
expect(mockCaptureException).toHaveBeenCalledTimes(0);
570-
571-
const btn = screen.getByTestId('errorBtn');
572-
fireEvent.click(btn);
568+
expect(mockCaptureException).toHaveBeenCalledTimes(0);
573569

574-
expect(mockCaptureException).toHaveBeenCalledTimes(1);
575-
expect(mockCaptureException).toHaveBeenLastCalledWith(expect.any(Object), {
576-
captureContext: {
577-
contexts: { react: { componentStack: expect.any(String) } },
578-
},
579-
mechanism: { handled: false },
580-
});
581-
});
570+
const btn = screen.getByTestId('errorBtn');
571+
fireEvent.click(btn);
572+
573+
expect(mockCaptureException).toHaveBeenCalledTimes(1);
574+
expect(mockCaptureException).toHaveBeenLastCalledWith(expect.any(Object), {
575+
captureContext: {
576+
contexts: { react: { componentStack: expect.any(String) } },
577+
},
578+
mechanism: { handled: expected },
579+
});
580+
},
581+
);
582582
});
583583
});

0 commit comments

Comments
 (0)