react-error-boundary 1.2.1 → 1.2.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,9 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright 2018 Brian Vaughn
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
6
+
7
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
8
+
9
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
package/README.md CHANGED
@@ -1,9 +1,19 @@
1
- # react-error-boundary
1
+ react-error-boundary
2
+ ====================
2
3
 
3
- Sample reusable React error boundary component for React 16+
4
+ **A simple, reusable React error boundary component for React 16+.**
4
5
 
5
- The simplest way to use a boundary is to wrap it around any component that may throw an error.
6
- This will handle errors thrown by that component's descendents also.
6
+ [![NPM registry](https://img.shields.io/npm/v/react-error-boundary.svg?style=for-the-badge)](https://yarnpkg.com/en/package/react-error-boundary)
7
+ [![NPM license](https://img.shields.io/npm/l/react-error-boundary.svg?style=for-the-badge)](LICENSE)
8
+
9
+ React [v16](https://reactjs.org/blog/2017/09/26/react-v16.0.html) introduced the concept of [“error boundaries”](https://reactjs.org/docs/error-boundaries.html).
10
+
11
+ This component provides a simple and reusable wrapper that you can use to wrap around your components. Any rendering errors in your components hierarchy can then be gracefully handled.
12
+
13
+ # Usage
14
+
15
+ The simplest way to use `<ErrorBoundary>` is to wrap it around any component that may throw an error.
16
+ This will handle errors thrown by that component and its descendants too.
7
17
 
8
18
  ```jsx
9
19
  import ErrorBoundary from 'react-error-boundary';
@@ -13,13 +23,14 @@ import ErrorBoundary from 'react-error-boundary';
13
23
  </ErrorBoundary>
14
24
  ```
15
25
 
16
- You can react to errors (eg for logging) by providing an `onError` callback:
26
+ You can react to errors (e.g. for logging) by providing an `onError` callback:
17
27
 
18
28
  ```jsx
19
29
  import ErrorBoundary from 'react-error-boundary';
20
30
 
21
31
  const myErrorHandler = (error: Error, componentStack: string) => {
22
- // ...
32
+ // Do something with the error
33
+ // E.g. log to an error logging client here
23
34
  };
24
35
 
25
36
  <ErrorBoundary onError={myErrorHandler}>
@@ -27,26 +38,38 @@ const myErrorHandler = (error: Error, componentStack: string) => {
27
38
  </ErrorBoundary>
28
39
  ```
29
40
 
30
- You can also customize the fallback appearance:
41
+ You can also customize the fallback component’s appearance:
31
42
 
32
43
  ```jsx
44
+ import { ErrorBoundary } from 'react-error-boundary';
45
+
33
46
  const MyFallbackComponent = ({ componentStack, error }) => (
34
- <div/>
35
- )
47
+ <div>
48
+ <p><strong>Oops! An error occured!</strong></p>
49
+ <p>Here’s what we know…</p>
50
+ <p><strong>Error:</strong> {error.toString()}</p>
51
+ <p><strong>Stacktrace:</strong> {componentStack}</p>
52
+ </div>
53
+ );
36
54
 
37
55
  <ErrorBoundary FallbackComponent={MyFallbackComponent}>
38
56
  <ComponentThatMayError />
39
57
  </ErrorBoundary>
40
58
  ```
41
59
 
42
- You can also use it as a HOC:
60
+ You can also use it as a [higher-order component](https://reactjs.org/docs/higher-order-components.html):
43
61
 
44
62
  ```jsx
45
- import {withErrorBoundary} from 'react-error-boundary';
63
+ import { ErrorBoundaryFallbackComponent, withErrorBoundary } from 'react-error-boundary';
46
64
 
47
65
  const ComponentWithErrorBoundary = withErrorBoundary(
48
- ComponentToDecorate: Element<any>,
49
- CustomFallbackComponent: ?Element<any>,
50
- onErrorHandler: ?(error: Error, componentStack: string) => void,
66
+ ComponentThatMayError,
67
+ ErrorBoundaryFallbackComponent, // Or pass in your own fallback component
68
+ onErrorHandler: (error, componentStack) => {
69
+ // Do something with the error
70
+ // E.g. log to an error logging client here
71
+ },
51
72
  );
52
- ```
73
+
74
+ <ComponentWithErrorBoundary />
75
+ ```
@@ -53,7 +53,8 @@ var ErrorBoundary = function (_Component) {
53
53
 
54
54
  if (typeof onError === 'function') {
55
55
  try {
56
- onError.call(this, error, info ? info.componentStack : "");
56
+ /* istanbul ignore next: Ignoring ternary; can’t reproduce missing info in test environment. */
57
+ onError.call(this, error, info ? info.componentStack : '');
57
58
  } catch (ignoredError) {}
58
59
  }
59
60
 
@@ -72,12 +73,14 @@ var ErrorBoundary = function (_Component) {
72
73
 
73
74
  if (error !== null) {
74
75
  return _react2.default.createElement(FallbackComponent, {
75
- componentStack: info ? info.componentStack : '',
76
+ componentStack:
77
+ // istanbul ignore next: Ignoring ternary; can’t reproduce missing info in test environment.
78
+ info ? info.componentStack : '',
76
79
  error: error
77
80
  });
78
81
  }
79
82
 
80
- return children;
83
+ return children || null;
81
84
  }
82
85
  }]);
83
86
 
@@ -94,13 +97,19 @@ ErrorBoundary.propTypes = {
94
97
  })
95
98
  };
96
99
  var withErrorBoundary = exports.withErrorBoundary = function withErrorBoundary(Component, FallbackComponent, onError) {
97
- return function (props) {
100
+ var Wrapped = function Wrapped(props) {
98
101
  return _react2.default.createElement(
99
102
  ErrorBoundary,
100
103
  { FallbackComponent: FallbackComponent, onError: onError },
101
104
  _react2.default.createElement(Component, props)
102
105
  );
103
106
  };
107
+
108
+ // Format for display in DevTools
109
+ var name = Component.displayName || Component.name;
110
+ Wrapped.displayName = name ? 'WithErrorBoundary(' + name + ')' : 'WithErrorBoundary';
111
+
112
+ return Wrapped;
104
113
  };
105
114
 
106
115
  withErrorBoundary.propTypes = babelPluginFlowReactPropTypes_proptype_ComponentType === require('prop-types').any ? {} : babelPluginFlowReactPropTypes_proptype_ComponentType;
@@ -0,0 +1,244 @@
1
+ 'use strict';
2
+
3
+ var _enzyme = require('enzyme');
4
+
5
+ var _react = require('react');
6
+
7
+ var _react2 = _interopRequireDefault(_react);
8
+
9
+ var _ErrorBoundary = require('../ErrorBoundary');
10
+
11
+ var _ErrorBoundary2 = _interopRequireDefault(_ErrorBoundary);
12
+
13
+ var _ErrorBoundaryFallbackComponent = require('../ErrorBoundaryFallbackComponent');
14
+
15
+ var _ErrorBoundaryFallbackComponent2 = _interopRequireDefault(_ErrorBoundaryFallbackComponent);
16
+
17
+ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
18
+
19
+ describe('ErrorBoundary', function () {
20
+ var consoleErrorSpy = void 0;
21
+ var mockError = void 0;
22
+
23
+ var Spell = void 0;
24
+ var Wand = void 0;
25
+
26
+ beforeAll(function () {
27
+ consoleErrorSpy = jest.spyOn(console, 'error').mockImplementation(function () {
28
+ // React error boundaries print the error to `console.error` stream even when it’s caught by our
29
+ // `ErrorBoundary` component. We suppress `console.error` to keep our test console output clean.
30
+ // @see #11098 Allow suppressing error boundary logs from intentionally thrown/caught errors
31
+ // https://github.com/facebook/react/issues/11098
32
+ });
33
+
34
+ mockError = new Error('You cast an unforgivable curse! You’ve earned a one-way ticket to Azkaban.');
35
+ Spell = function Spell(_ref) {
36
+ var incantation = _ref.incantation;
37
+
38
+ switch (incantation) {
39
+ case 'Avada Kedavra':
40
+ case 'Crucio':
41
+ case 'Imperio':
42
+ throw mockError;
43
+
44
+ default:
45
+ return _react2.default.createElement(
46
+ 'p',
47
+ null,
48
+ 'You cast the ' + incantation + ' spell!'
49
+ );
50
+ }
51
+ };
52
+
53
+ Wand = function Wand(_ref2) {
54
+ var name = _ref2.name,
55
+ incantation = _ref2.incantation;
56
+ return _react2.default.createElement(
57
+ 'div',
58
+ null,
59
+ _react2.default.createElement(
60
+ 'p',
61
+ null,
62
+ 'Casting spell with the ' + name + ' wand'
63
+ ),
64
+ _react2.default.createElement(Spell, { incantation: incantation })
65
+ );
66
+ };
67
+ });
68
+
69
+ afterAll(function () {
70
+ consoleErrorSpy.mockRestore();
71
+ });
72
+
73
+ it('Renders the child component if there is no error', function () {
74
+ var wrapper = (0, _enzyme.mount)(_react2.default.createElement(
75
+ _ErrorBoundary2.default,
76
+ null,
77
+ _react2.default.createElement(Wand, { name: 'Harry\u2019s', incantation: 'Expelliarmus' })
78
+ ));
79
+
80
+ var WandWithErrorBoundary = (0, _ErrorBoundary.withErrorBoundary)(Wand);
81
+ var wrapperWithErrorBoundary = (0, _enzyme.mount)(_react2.default.createElement(WandWithErrorBoundary, { name: 'Harry\u2019s', incantation: 'Expelliarmus' }));
82
+
83
+ expect(wrapper.state().error).toBe(null);
84
+ expect(wrapper.state().info).toBe(null);
85
+ expect(wrapper.containsMatchingElement(_react2.default.createElement(_ErrorBoundaryFallbackComponent2.default, null))).toBe(false);
86
+ expect(wrapper.contains(_react2.default.createElement(Wand, { name: 'Harry\u2019s', incantation: 'Expelliarmus' }))).toBe(true);
87
+
88
+ // Note: We use `….instance().state …` instead of `….state() …` here because…
89
+ // > ReactWrapper::state() can only be called on the root
90
+ expect(wrapperWithErrorBoundary.find(_ErrorBoundary2.default).instance().state.error).toBe(null);
91
+ expect(wrapperWithErrorBoundary.find(_ErrorBoundary2.default).instance().state.info).toBe(null);
92
+ expect(wrapperWithErrorBoundary.containsMatchingElement(_react2.default.createElement(_ErrorBoundaryFallbackComponent2.default, null))).toBe(false);
93
+ expect(wrapperWithErrorBoundary.contains(_react2.default.createElement(Wand, { name: 'Harry\u2019s', incantation: 'Expelliarmus' }))).toBe(true);
94
+ });
95
+
96
+ it('Sets its state to an error state and renders the default fallback component', function () {
97
+ var wrapper = (0, _enzyme.mount)(_react2.default.createElement(
98
+ _ErrorBoundary2.default,
99
+ null,
100
+ _react2.default.createElement(Wand, { name: 'Voldemort\u2019s', incantation: 'Avada Kedavra' })
101
+ ));
102
+
103
+ var WandWithErrorBoundary = (0, _ErrorBoundary.withErrorBoundary)(Wand);
104
+ var wrapperWithErrorBoundary = (0, _enzyme.mount)(_react2.default.createElement(WandWithErrorBoundary, { name: 'Voldemort\u2019s', incantation: 'Avada Kedavra' }));
105
+
106
+ expect(wrapper.state().error).toEqual(expect.objectContaining(mockError));
107
+ expect(wrapper.state().info).toEqual(expect.objectContaining({
108
+ componentStack: expect.any(String)
109
+ }));
110
+ expect(wrapper.containsMatchingElement(_react2.default.createElement(_ErrorBoundaryFallbackComponent2.default, null))).toBe(true);
111
+
112
+ expect(wrapperWithErrorBoundary.find(_ErrorBoundary2.default).instance().state.error).toEqual(expect.objectContaining(mockError));
113
+ expect(wrapperWithErrorBoundary.find(_ErrorBoundary2.default).instance().state.info).toEqual(expect.objectContaining({
114
+ componentStack: expect.any(String)
115
+ }));
116
+ expect(wrapperWithErrorBoundary.containsMatchingElement(_react2.default.createElement(_ErrorBoundaryFallbackComponent2.default, null))).toBe(true);
117
+ });
118
+
119
+ it('Sets its state to an error state and renders a custom fallback component', function () {
120
+ var MockFallbackComponent = function MockFallbackComponent(_ref3) {
121
+ var error = _ref3.error,
122
+ componentStack = _ref3.componentStack;
123
+ return _react2.default.createElement(
124
+ 'div',
125
+ null,
126
+ _react2.default.createElement(
127
+ 'p',
128
+ null,
129
+ _react2.default.createElement(
130
+ 'strong',
131
+ null,
132
+ 'Wand unable to perform magic!'
133
+ ),
134
+ 'Please contact Ollivanders in Diagon Alley for repairs.'
135
+ ),
136
+ _react2.default.createElement(
137
+ 'p',
138
+ null,
139
+ _react2.default.createElement(
140
+ 'strong',
141
+ null,
142
+ 'Error:'
143
+ ),
144
+ ' ',
145
+ error.toString()
146
+ ),
147
+ _react2.default.createElement(
148
+ 'p',
149
+ null,
150
+ _react2.default.createElement(
151
+ 'strong',
152
+ null,
153
+ 'Stacktrace:'
154
+ ),
155
+ _react2.default.createElement(
156
+ 'pre',
157
+ null,
158
+ componentStack
159
+ )
160
+ )
161
+ );
162
+ };
163
+
164
+ var wrapper = (0, _enzyme.mount)(_react2.default.createElement(
165
+ _ErrorBoundary2.default,
166
+ { FallbackComponent: MockFallbackComponent },
167
+ _react2.default.createElement(Wand, { name: 'Voldemort\u2019s', incantation: 'Crucio' })
168
+ ));
169
+
170
+ var WandWithErrorBoundary = (0, _ErrorBoundary.withErrorBoundary)(Wand, MockFallbackComponent);
171
+ var wrapperWithErrorBoundary = (0, _enzyme.mount)(_react2.default.createElement(WandWithErrorBoundary, { name: 'Voldemort\u2019s', incantation: 'Crucio' }));
172
+
173
+ expect(wrapper.state().error).toEqual(expect.objectContaining(mockError));
174
+ expect(wrapper.state().info).toEqual(expect.objectContaining({
175
+ componentStack: expect.any(String)
176
+ }));
177
+ expect(wrapper.containsMatchingElement(_react2.default.createElement(_ErrorBoundaryFallbackComponent2.default, null))).toBe(false);
178
+ expect(wrapper.containsMatchingElement(_react2.default.createElement(MockFallbackComponent, {
179
+ error: mockError /* componentStack="ignored" */
180
+ }))).toBe(true);
181
+
182
+ expect(wrapperWithErrorBoundary.find(_ErrorBoundary2.default).instance().state.error).toEqual(expect.objectContaining(mockError));
183
+ expect(wrapperWithErrorBoundary.find(_ErrorBoundary2.default).instance().state.info).toEqual(expect.objectContaining({
184
+ componentStack: expect.any(String)
185
+ }));
186
+ expect(wrapperWithErrorBoundary.containsMatchingElement(_react2.default.createElement(_ErrorBoundaryFallbackComponent2.default, null))).toBe(false);
187
+ expect(wrapperWithErrorBoundary.containsMatchingElement(_react2.default.createElement(MockFallbackComponent, {
188
+ error: mockError /* componentStack="ignored" */
189
+ }))).toBe(true);
190
+ });
191
+
192
+ it('Sets its state to an error state and invokes the onError callback prop', function () {
193
+ var mockOnError = jest.fn().mockImplementation(function (error, // eslint-disable-line no-unused-vars
194
+ info) // eslint-disable-line no-unused-vars
195
+ {});
196
+
197
+ var mockOnErrorWithErrorBoundary = jest.fn().mockImplementation(function (error, // eslint-disable-line no-unused-vars
198
+ info) // eslint-disable-line no-unused-vars
199
+ {});
200
+
201
+ var wrapper = (0, _enzyme.mount)(_react2.default.createElement(
202
+ _ErrorBoundary2.default,
203
+ { onError: mockOnError },
204
+ _react2.default.createElement(Wand, { name: 'Voldemort\u2019s', incantation: 'Imperio' })
205
+ ));
206
+ var WandWithErrorBoundary = (0, _ErrorBoundary.withErrorBoundary)(Wand, _ErrorBoundaryFallbackComponent2.default, mockOnErrorWithErrorBoundary);
207
+ var wrapperWithErrorBoundary = (0, _enzyme.mount)(_react2.default.createElement(WandWithErrorBoundary, { name: 'Voldemort\u2019s', incantation: 'Imperio' }));
208
+
209
+ expect(wrapper.state().error).toEqual(expect.objectContaining(mockError));
210
+ expect(wrapper.state().info).toEqual(expect.objectContaining({
211
+ componentStack: expect.any(String)
212
+ }));
213
+ expect(mockOnError).toHaveBeenCalledWith(mockError, expect.any(String));
214
+ expect(wrapper.containsMatchingElement(_react2.default.createElement(_ErrorBoundaryFallbackComponent2.default, {
215
+ error: mockError /* componentStack="ignored" */
216
+ }))).toBe(true);
217
+
218
+ expect(wrapperWithErrorBoundary.find(_ErrorBoundary2.default).instance().state.error).toEqual(expect.objectContaining(mockError));
219
+ expect(wrapperWithErrorBoundary.find(_ErrorBoundary2.default).instance().state.info).toEqual(expect.objectContaining({
220
+ componentStack: expect.any(String)
221
+ }));
222
+ expect(mockOnErrorWithErrorBoundary).toHaveBeenCalledWith(mockError, expect.any(String));
223
+ expect(wrapperWithErrorBoundary.containsMatchingElement(_react2.default.createElement(_ErrorBoundaryFallbackComponent2.default, {
224
+ error: mockError /* componentStack="ignored" */
225
+ }))).toBe(true);
226
+ });
227
+
228
+ it('sets the correct displayName for wrapped components', function () {
229
+ function NormalComponent() {
230
+ return null;
231
+ }
232
+ expect((0, _ErrorBoundary.withErrorBoundary)(NormalComponent).displayName).toBe('WithErrorBoundary(NormalComponent)');
233
+
234
+ function ComponentWithDisplayNameOverride() {
235
+ return null;
236
+ }
237
+ ComponentWithDisplayNameOverride.displayName = 'Override';
238
+
239
+ expect((0, _ErrorBoundary.withErrorBoundary)(ComponentWithDisplayNameOverride).displayName).toBe('WithErrorBoundary(Override)');
240
+ expect((0, _ErrorBoundary.withErrorBoundary)(function () {
241
+ return null;
242
+ }).displayName).toBe('WithErrorBoundary');
243
+ });
244
+ });
@@ -0,0 +1,27 @@
1
+ 'use strict';
2
+
3
+ var _react = require('react');
4
+
5
+ var _react2 = _interopRequireDefault(_react);
6
+
7
+ var _enzyme = require('enzyme');
8
+
9
+ var _ErrorBoundaryFallbackComponent = require('../ErrorBoundaryFallbackComponent');
10
+
11
+ var _ErrorBoundaryFallbackComponent2 = _interopRequireDefault(_ErrorBoundaryFallbackComponent);
12
+
13
+ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
14
+
15
+ describe('ErrorBoundaryFallbackComponent', function () {
16
+ var mockError = void 0;
17
+
18
+ beforeAll(function () {
19
+ mockError = new Error('You cast an unforgivable curse! You’ve earned a one-way ticket to Azkaban.');
20
+ });
21
+
22
+ it('Passes a snapshot test', function () {
23
+ var wrapper = (0, _enzyme.shallow)(_react2.default.createElement(_ErrorBoundaryFallbackComponent2.default, { error: mockError, componentStack: '' }));
24
+
25
+ expect(wrapper).toMatchSnapshot();
26
+ });
27
+ });
@@ -0,0 +1,213 @@
1
+ // Jest Snapshot v1, https://goo.gl/fbAQLP
2
+
3
+ exports[`ErrorBoundaryFallbackComponent Passes a snapshot test 1`] = `
4
+ ShallowWrapper {
5
+ Symbol(enzyme.__root__): [Circular],
6
+ Symbol(enzyme.__unrendered__): <ErrorBoundaryFallbackComponent
7
+ componentStack=""
8
+ error={[Error: You cast an unforgivable curse! You’ve earned a one-way ticket to Azkaban.]}
9
+ />,
10
+ Symbol(enzyme.__renderer__): Object {
11
+ "batchedUpdates": [Function],
12
+ "getNode": [Function],
13
+ "render": [Function],
14
+ "simulateEvent": [Function],
15
+ "unmount": [Function],
16
+ },
17
+ Symbol(enzyme.__node__): Object {
18
+ "instance": null,
19
+ "key": undefined,
20
+ "nodeType": "host",
21
+ "props": Object {
22
+ "children": <svg
23
+ preserveAspectRatio="xMidYMid"
24
+ style={
25
+ Object {
26
+ "fill": "currentColor",
27
+ "flex": "1 1 auto",
28
+ }
29
+ }
30
+ viewBox="0 0 24 24"
31
+ >
32
+ <path
33
+ d="M20,12A8,8 0 0,0 12,4A8,8 0 0,0 4,12A8,8 0 0,0 12,20A8,8 0 0,0 20,
34
+ 12M22,12A10,10 0 0,1 12,22A10,10 0 0,1 2,12A10,10 0 0,1 12,2A10,10 0 0,1 22,
35
+ 12M15.5,8C16.3,8 17,8.7 17,9.5C17,10.3 16.3,11 15.5,11C14.7,11 14,10.3 14,
36
+ 9.5C14,8.7 14.7,8 15.5,8M10,9.5C10,10.3 9.3,11 8.5,11C7.7,11 7,10.3 7,9.5C7,
37
+ 8.7 7.7,8 8.5,8C9.3,8 10,8.7 10,9.5M12,14C13.75,14 15.29,14.72 16.19,
38
+ 15.81L14.77,17.23C14.32,16.5 13.25,16 12,16C10.75,16 9.68,16.5 9.23,
39
+ 17.23L7.81,15.81C8.71,14.72 10.25,14 12,14Z"
40
+ />
41
+ </svg>,
42
+ "style": Object {
43
+ "alignItems": "center",
44
+ "backgroundColor": "#C00",
45
+ "boxSizing": "border-box",
46
+ "color": "#FFF",
47
+ "cursor": "help",
48
+ "display": "flex",
49
+ "flexDirection": "column",
50
+ "height": "100%",
51
+ "maxHeight": "100vh",
52
+ "maxWidth": "100vw",
53
+ "textAlign": "center",
54
+ "width": "100%",
55
+ },
56
+ "title": "Error: You cast an unforgivable curse! You’ve earned a one-way ticket to Azkaban.
57
+
58
+ This is located at:",
59
+ },
60
+ "ref": null,
61
+ "rendered": Object {
62
+ "instance": null,
63
+ "key": undefined,
64
+ "nodeType": "host",
65
+ "props": Object {
66
+ "children": <path
67
+ d="M20,12A8,8 0 0,0 12,4A8,8 0 0,0 4,12A8,8 0 0,0 12,20A8,8 0 0,0 20,
68
+ 12M22,12A10,10 0 0,1 12,22A10,10 0 0,1 2,12A10,10 0 0,1 12,2A10,10 0 0,1 22,
69
+ 12M15.5,8C16.3,8 17,8.7 17,9.5C17,10.3 16.3,11 15.5,11C14.7,11 14,10.3 14,
70
+ 9.5C14,8.7 14.7,8 15.5,8M10,9.5C10,10.3 9.3,11 8.5,11C7.7,11 7,10.3 7,9.5C7,
71
+ 8.7 7.7,8 8.5,8C9.3,8 10,8.7 10,9.5M12,14C13.75,14 15.29,14.72 16.19,
72
+ 15.81L14.77,17.23C14.32,16.5 13.25,16 12,16C10.75,16 9.68,16.5 9.23,
73
+ 17.23L7.81,15.81C8.71,14.72 10.25,14 12,14Z"
74
+ />,
75
+ "preserveAspectRatio": "xMidYMid",
76
+ "style": Object {
77
+ "fill": "currentColor",
78
+ "flex": "1 1 auto",
79
+ },
80
+ "viewBox": "0 0 24 24",
81
+ },
82
+ "ref": null,
83
+ "rendered": Object {
84
+ "instance": null,
85
+ "key": undefined,
86
+ "nodeType": "host",
87
+ "props": Object {
88
+ "d": "M20,12A8,8 0 0,0 12,4A8,8 0 0,0 4,12A8,8 0 0,0 12,20A8,8 0 0,0 20,
89
+ 12M22,12A10,10 0 0,1 12,22A10,10 0 0,1 2,12A10,10 0 0,1 12,2A10,10 0 0,1 22,
90
+ 12M15.5,8C16.3,8 17,8.7 17,9.5C17,10.3 16.3,11 15.5,11C14.7,11 14,10.3 14,
91
+ 9.5C14,8.7 14.7,8 15.5,8M10,9.5C10,10.3 9.3,11 8.5,11C7.7,11 7,10.3 7,9.5C7,
92
+ 8.7 7.7,8 8.5,8C9.3,8 10,8.7 10,9.5M12,14C13.75,14 15.29,14.72 16.19,
93
+ 15.81L14.77,17.23C14.32,16.5 13.25,16 12,16C10.75,16 9.68,16.5 9.23,
94
+ 17.23L7.81,15.81C8.71,14.72 10.25,14 12,14Z",
95
+ },
96
+ "ref": null,
97
+ "rendered": null,
98
+ "type": "path",
99
+ },
100
+ "type": "svg",
101
+ },
102
+ "type": "div",
103
+ },
104
+ Symbol(enzyme.__nodes__): Array [
105
+ Object {
106
+ "instance": null,
107
+ "key": undefined,
108
+ "nodeType": "host",
109
+ "props": Object {
110
+ "children": <svg
111
+ preserveAspectRatio="xMidYMid"
112
+ style={
113
+ Object {
114
+ "fill": "currentColor",
115
+ "flex": "1 1 auto",
116
+ }
117
+ }
118
+ viewBox="0 0 24 24"
119
+ >
120
+ <path
121
+ d="M20,12A8,8 0 0,0 12,4A8,8 0 0,0 4,12A8,8 0 0,0 12,20A8,8 0 0,0 20,
122
+ 12M22,12A10,10 0 0,1 12,22A10,10 0 0,1 2,12A10,10 0 0,1 12,2A10,10 0 0,1 22,
123
+ 12M15.5,8C16.3,8 17,8.7 17,9.5C17,10.3 16.3,11 15.5,11C14.7,11 14,10.3 14,
124
+ 9.5C14,8.7 14.7,8 15.5,8M10,9.5C10,10.3 9.3,11 8.5,11C7.7,11 7,10.3 7,9.5C7,
125
+ 8.7 7.7,8 8.5,8C9.3,8 10,8.7 10,9.5M12,14C13.75,14 15.29,14.72 16.19,
126
+ 15.81L14.77,17.23C14.32,16.5 13.25,16 12,16C10.75,16 9.68,16.5 9.23,
127
+ 17.23L7.81,15.81C8.71,14.72 10.25,14 12,14Z"
128
+ />
129
+ </svg>,
130
+ "style": Object {
131
+ "alignItems": "center",
132
+ "backgroundColor": "#C00",
133
+ "boxSizing": "border-box",
134
+ "color": "#FFF",
135
+ "cursor": "help",
136
+ "display": "flex",
137
+ "flexDirection": "column",
138
+ "height": "100%",
139
+ "maxHeight": "100vh",
140
+ "maxWidth": "100vw",
141
+ "textAlign": "center",
142
+ "width": "100%",
143
+ },
144
+ "title": "Error: You cast an unforgivable curse! You’ve earned a one-way ticket to Azkaban.
145
+
146
+ This is located at:",
147
+ },
148
+ "ref": null,
149
+ "rendered": Object {
150
+ "instance": null,
151
+ "key": undefined,
152
+ "nodeType": "host",
153
+ "props": Object {
154
+ "children": <path
155
+ d="M20,12A8,8 0 0,0 12,4A8,8 0 0,0 4,12A8,8 0 0,0 12,20A8,8 0 0,0 20,
156
+ 12M22,12A10,10 0 0,1 12,22A10,10 0 0,1 2,12A10,10 0 0,1 12,2A10,10 0 0,1 22,
157
+ 12M15.5,8C16.3,8 17,8.7 17,9.5C17,10.3 16.3,11 15.5,11C14.7,11 14,10.3 14,
158
+ 9.5C14,8.7 14.7,8 15.5,8M10,9.5C10,10.3 9.3,11 8.5,11C7.7,11 7,10.3 7,9.5C7,
159
+ 8.7 7.7,8 8.5,8C9.3,8 10,8.7 10,9.5M12,14C13.75,14 15.29,14.72 16.19,
160
+ 15.81L14.77,17.23C14.32,16.5 13.25,16 12,16C10.75,16 9.68,16.5 9.23,
161
+ 17.23L7.81,15.81C8.71,14.72 10.25,14 12,14Z"
162
+ />,
163
+ "preserveAspectRatio": "xMidYMid",
164
+ "style": Object {
165
+ "fill": "currentColor",
166
+ "flex": "1 1 auto",
167
+ },
168
+ "viewBox": "0 0 24 24",
169
+ },
170
+ "ref": null,
171
+ "rendered": Object {
172
+ "instance": null,
173
+ "key": undefined,
174
+ "nodeType": "host",
175
+ "props": Object {
176
+ "d": "M20,12A8,8 0 0,0 12,4A8,8 0 0,0 4,12A8,8 0 0,0 12,20A8,8 0 0,0 20,
177
+ 12M22,12A10,10 0 0,1 12,22A10,10 0 0,1 2,12A10,10 0 0,1 12,2A10,10 0 0,1 22,
178
+ 12M15.5,8C16.3,8 17,8.7 17,9.5C17,10.3 16.3,11 15.5,11C14.7,11 14,10.3 14,
179
+ 9.5C14,8.7 14.7,8 15.5,8M10,9.5C10,10.3 9.3,11 8.5,11C7.7,11 7,10.3 7,9.5C7,
180
+ 8.7 7.7,8 8.5,8C9.3,8 10,8.7 10,9.5M12,14C13.75,14 15.29,14.72 16.19,
181
+ 15.81L14.77,17.23C14.32,16.5 13.25,16 12,16C10.75,16 9.68,16.5 9.23,
182
+ 17.23L7.81,15.81C8.71,14.72 10.25,14 12,14Z",
183
+ },
184
+ "ref": null,
185
+ "rendered": null,
186
+ "type": "path",
187
+ },
188
+ "type": "svg",
189
+ },
190
+ "type": "div",
191
+ },
192
+ ],
193
+ Symbol(enzyme.__options__): Object {
194
+ "adapter": ReactSixteenAdapter {
195
+ "options": Object {
196
+ "enableComponentDidUpdateOnSetState": true,
197
+ "lifecycles": Object {
198
+ "componentDidUpdate": Object {
199
+ "onSetState": true,
200
+ },
201
+ "getDerivedStateFromProps": true,
202
+ "getSnapshotBeforeUpdate": true,
203
+ "setState": Object {
204
+ "skipsComponentDidUpdateOnNullish": true,
205
+ },
206
+ },
207
+ },
208
+ },
209
+ "attachTo": undefined,
210
+ "hydrateIn": undefined,
211
+ },
212
+ }
213
+ `;
@@ -0,0 +1,13 @@
1
+ 'use strict';
2
+
3
+ var _enzyme = require('enzyme');
4
+
5
+ var _enzyme2 = _interopRequireDefault(_enzyme);
6
+
7
+ var _enzymeAdapterReact = require('enzyme-adapter-react-16');
8
+
9
+ var _enzymeAdapterReact2 = _interopRequireDefault(_enzymeAdapterReact);
10
+
11
+ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
12
+
13
+ _enzyme2.default.configure({ adapter: new _enzymeAdapterReact2.default() });
@@ -36,7 +36,8 @@ var ErrorBoundary = function (_Component) {
36
36
 
37
37
  if (typeof onError === 'function') {
38
38
  try {
39
- onError.call(this, error, info ? info.componentStack : "");
39
+ /* istanbul ignore next: Ignoring ternary; can’t reproduce missing info in test environment. */
40
+ onError.call(this, error, info ? info.componentStack : '');
40
41
  } catch (ignoredError) {}
41
42
  }
42
43
 
@@ -55,12 +56,14 @@ var ErrorBoundary = function (_Component) {
55
56
 
56
57
  if (error !== null) {
57
58
  return React.createElement(FallbackComponent, {
58
- componentStack: info ? info.componentStack : '',
59
+ componentStack:
60
+ // istanbul ignore next: Ignoring ternary; can’t reproduce missing info in test environment.
61
+ info ? info.componentStack : '',
59
62
  error: error
60
63
  });
61
64
  }
62
65
 
63
- return children;
66
+ return children || null;
64
67
  }
65
68
  }]);
66
69
 
@@ -79,13 +82,19 @@ ErrorBoundary.propTypes = {
79
82
 
80
83
 
81
84
  export var withErrorBoundary = function withErrorBoundary(Component, FallbackComponent, onError) {
82
- return function (props) {
85
+ var Wrapped = function Wrapped(props) {
83
86
  return React.createElement(
84
87
  ErrorBoundary,
85
88
  { FallbackComponent: FallbackComponent, onError: onError },
86
89
  React.createElement(Component, props)
87
90
  );
88
91
  };
92
+
93
+ // Format for display in DevTools
94
+ var name = Component.displayName || Component.name;
95
+ Wrapped.displayName = name ? 'WithErrorBoundary(' + name + ')' : 'WithErrorBoundary';
96
+
97
+ return Wrapped;
89
98
  };
90
99
 
91
100
  withErrorBoundary.propTypes = babelPluginFlowReactPropTypes_proptype_ComponentType === require('prop-types').any ? {} : babelPluginFlowReactPropTypes_proptype_ComponentType;
@@ -0,0 +1,232 @@
1
+ import { mount } from 'enzyme';
2
+ import React from 'react';
3
+
4
+ import ErrorBoundary, { withErrorBoundary } from '../ErrorBoundary';
5
+ import ErrorBoundaryFallbackComponent from '../ErrorBoundaryFallbackComponent';
6
+
7
+ describe('ErrorBoundary', function () {
8
+ var consoleErrorSpy = void 0;
9
+ var mockError = void 0;
10
+
11
+ var Spell = void 0;
12
+ var Wand = void 0;
13
+
14
+ beforeAll(function () {
15
+ consoleErrorSpy = jest.spyOn(console, 'error').mockImplementation(function () {
16
+ // React error boundaries print the error to `console.error` stream even when it’s caught by our
17
+ // `ErrorBoundary` component. We suppress `console.error` to keep our test console output clean.
18
+ // @see #11098 Allow suppressing error boundary logs from intentionally thrown/caught errors
19
+ // https://github.com/facebook/react/issues/11098
20
+ });
21
+
22
+ mockError = new Error('You cast an unforgivable curse! You’ve earned a one-way ticket to Azkaban.');
23
+ Spell = function Spell(_ref) {
24
+ var incantation = _ref.incantation;
25
+
26
+ switch (incantation) {
27
+ case 'Avada Kedavra':
28
+ case 'Crucio':
29
+ case 'Imperio':
30
+ throw mockError;
31
+
32
+ default:
33
+ return React.createElement(
34
+ 'p',
35
+ null,
36
+ 'You cast the ' + incantation + ' spell!'
37
+ );
38
+ }
39
+ };
40
+
41
+ Wand = function Wand(_ref2) {
42
+ var name = _ref2.name,
43
+ incantation = _ref2.incantation;
44
+ return React.createElement(
45
+ 'div',
46
+ null,
47
+ React.createElement(
48
+ 'p',
49
+ null,
50
+ 'Casting spell with the ' + name + ' wand'
51
+ ),
52
+ React.createElement(Spell, { incantation: incantation })
53
+ );
54
+ };
55
+ });
56
+
57
+ afterAll(function () {
58
+ consoleErrorSpy.mockRestore();
59
+ });
60
+
61
+ it('Renders the child component if there is no error', function () {
62
+ var wrapper = mount(React.createElement(
63
+ ErrorBoundary,
64
+ null,
65
+ React.createElement(Wand, { name: 'Harry\u2019s', incantation: 'Expelliarmus' })
66
+ ));
67
+
68
+ var WandWithErrorBoundary = withErrorBoundary(Wand);
69
+ var wrapperWithErrorBoundary = mount(React.createElement(WandWithErrorBoundary, { name: 'Harry\u2019s', incantation: 'Expelliarmus' }));
70
+
71
+ expect(wrapper.state().error).toBe(null);
72
+ expect(wrapper.state().info).toBe(null);
73
+ expect(wrapper.containsMatchingElement(React.createElement(ErrorBoundaryFallbackComponent, null))).toBe(false);
74
+ expect(wrapper.contains(React.createElement(Wand, { name: 'Harry\u2019s', incantation: 'Expelliarmus' }))).toBe(true);
75
+
76
+ // Note: We use `….instance().state …` instead of `….state() …` here because…
77
+ // > ReactWrapper::state() can only be called on the root
78
+ expect(wrapperWithErrorBoundary.find(ErrorBoundary).instance().state.error).toBe(null);
79
+ expect(wrapperWithErrorBoundary.find(ErrorBoundary).instance().state.info).toBe(null);
80
+ expect(wrapperWithErrorBoundary.containsMatchingElement(React.createElement(ErrorBoundaryFallbackComponent, null))).toBe(false);
81
+ expect(wrapperWithErrorBoundary.contains(React.createElement(Wand, { name: 'Harry\u2019s', incantation: 'Expelliarmus' }))).toBe(true);
82
+ });
83
+
84
+ it('Sets its state to an error state and renders the default fallback component', function () {
85
+ var wrapper = mount(React.createElement(
86
+ ErrorBoundary,
87
+ null,
88
+ React.createElement(Wand, { name: 'Voldemort\u2019s', incantation: 'Avada Kedavra' })
89
+ ));
90
+
91
+ var WandWithErrorBoundary = withErrorBoundary(Wand);
92
+ var wrapperWithErrorBoundary = mount(React.createElement(WandWithErrorBoundary, { name: 'Voldemort\u2019s', incantation: 'Avada Kedavra' }));
93
+
94
+ expect(wrapper.state().error).toEqual(expect.objectContaining(mockError));
95
+ expect(wrapper.state().info).toEqual(expect.objectContaining({
96
+ componentStack: expect.any(String)
97
+ }));
98
+ expect(wrapper.containsMatchingElement(React.createElement(ErrorBoundaryFallbackComponent, null))).toBe(true);
99
+
100
+ expect(wrapperWithErrorBoundary.find(ErrorBoundary).instance().state.error).toEqual(expect.objectContaining(mockError));
101
+ expect(wrapperWithErrorBoundary.find(ErrorBoundary).instance().state.info).toEqual(expect.objectContaining({
102
+ componentStack: expect.any(String)
103
+ }));
104
+ expect(wrapperWithErrorBoundary.containsMatchingElement(React.createElement(ErrorBoundaryFallbackComponent, null))).toBe(true);
105
+ });
106
+
107
+ it('Sets its state to an error state and renders a custom fallback component', function () {
108
+ var MockFallbackComponent = function MockFallbackComponent(_ref3) {
109
+ var error = _ref3.error,
110
+ componentStack = _ref3.componentStack;
111
+ return React.createElement(
112
+ 'div',
113
+ null,
114
+ React.createElement(
115
+ 'p',
116
+ null,
117
+ React.createElement(
118
+ 'strong',
119
+ null,
120
+ 'Wand unable to perform magic!'
121
+ ),
122
+ 'Please contact Ollivanders in Diagon Alley for repairs.'
123
+ ),
124
+ React.createElement(
125
+ 'p',
126
+ null,
127
+ React.createElement(
128
+ 'strong',
129
+ null,
130
+ 'Error:'
131
+ ),
132
+ ' ',
133
+ error.toString()
134
+ ),
135
+ React.createElement(
136
+ 'p',
137
+ null,
138
+ React.createElement(
139
+ 'strong',
140
+ null,
141
+ 'Stacktrace:'
142
+ ),
143
+ React.createElement(
144
+ 'pre',
145
+ null,
146
+ componentStack
147
+ )
148
+ )
149
+ );
150
+ };
151
+
152
+ var wrapper = mount(React.createElement(
153
+ ErrorBoundary,
154
+ { FallbackComponent: MockFallbackComponent },
155
+ React.createElement(Wand, { name: 'Voldemort\u2019s', incantation: 'Crucio' })
156
+ ));
157
+
158
+ var WandWithErrorBoundary = withErrorBoundary(Wand, MockFallbackComponent);
159
+ var wrapperWithErrorBoundary = mount(React.createElement(WandWithErrorBoundary, { name: 'Voldemort\u2019s', incantation: 'Crucio' }));
160
+
161
+ expect(wrapper.state().error).toEqual(expect.objectContaining(mockError));
162
+ expect(wrapper.state().info).toEqual(expect.objectContaining({
163
+ componentStack: expect.any(String)
164
+ }));
165
+ expect(wrapper.containsMatchingElement(React.createElement(ErrorBoundaryFallbackComponent, null))).toBe(false);
166
+ expect(wrapper.containsMatchingElement(React.createElement(MockFallbackComponent, {
167
+ error: mockError /* componentStack="ignored" */
168
+ }))).toBe(true);
169
+
170
+ expect(wrapperWithErrorBoundary.find(ErrorBoundary).instance().state.error).toEqual(expect.objectContaining(mockError));
171
+ expect(wrapperWithErrorBoundary.find(ErrorBoundary).instance().state.info).toEqual(expect.objectContaining({
172
+ componentStack: expect.any(String)
173
+ }));
174
+ expect(wrapperWithErrorBoundary.containsMatchingElement(React.createElement(ErrorBoundaryFallbackComponent, null))).toBe(false);
175
+ expect(wrapperWithErrorBoundary.containsMatchingElement(React.createElement(MockFallbackComponent, {
176
+ error: mockError /* componentStack="ignored" */
177
+ }))).toBe(true);
178
+ });
179
+
180
+ it('Sets its state to an error state and invokes the onError callback prop', function () {
181
+ var mockOnError = jest.fn().mockImplementation(function (error, // eslint-disable-line no-unused-vars
182
+ info) // eslint-disable-line no-unused-vars
183
+ {});
184
+
185
+ var mockOnErrorWithErrorBoundary = jest.fn().mockImplementation(function (error, // eslint-disable-line no-unused-vars
186
+ info) // eslint-disable-line no-unused-vars
187
+ {});
188
+
189
+ var wrapper = mount(React.createElement(
190
+ ErrorBoundary,
191
+ { onError: mockOnError },
192
+ React.createElement(Wand, { name: 'Voldemort\u2019s', incantation: 'Imperio' })
193
+ ));
194
+ var WandWithErrorBoundary = withErrorBoundary(Wand, ErrorBoundaryFallbackComponent, mockOnErrorWithErrorBoundary);
195
+ var wrapperWithErrorBoundary = mount(React.createElement(WandWithErrorBoundary, { name: 'Voldemort\u2019s', incantation: 'Imperio' }));
196
+
197
+ expect(wrapper.state().error).toEqual(expect.objectContaining(mockError));
198
+ expect(wrapper.state().info).toEqual(expect.objectContaining({
199
+ componentStack: expect.any(String)
200
+ }));
201
+ expect(mockOnError).toHaveBeenCalledWith(mockError, expect.any(String));
202
+ expect(wrapper.containsMatchingElement(React.createElement(ErrorBoundaryFallbackComponent, {
203
+ error: mockError /* componentStack="ignored" */
204
+ }))).toBe(true);
205
+
206
+ expect(wrapperWithErrorBoundary.find(ErrorBoundary).instance().state.error).toEqual(expect.objectContaining(mockError));
207
+ expect(wrapperWithErrorBoundary.find(ErrorBoundary).instance().state.info).toEqual(expect.objectContaining({
208
+ componentStack: expect.any(String)
209
+ }));
210
+ expect(mockOnErrorWithErrorBoundary).toHaveBeenCalledWith(mockError, expect.any(String));
211
+ expect(wrapperWithErrorBoundary.containsMatchingElement(React.createElement(ErrorBoundaryFallbackComponent, {
212
+ error: mockError /* componentStack="ignored" */
213
+ }))).toBe(true);
214
+ });
215
+
216
+ it('sets the correct displayName for wrapped components', function () {
217
+ function NormalComponent() {
218
+ return null;
219
+ }
220
+ expect(withErrorBoundary(NormalComponent).displayName).toBe('WithErrorBoundary(NormalComponent)');
221
+
222
+ function ComponentWithDisplayNameOverride() {
223
+ return null;
224
+ }
225
+ ComponentWithDisplayNameOverride.displayName = 'Override';
226
+
227
+ expect(withErrorBoundary(ComponentWithDisplayNameOverride).displayName).toBe('WithErrorBoundary(Override)');
228
+ expect(withErrorBoundary(function () {
229
+ return null;
230
+ }).displayName).toBe('WithErrorBoundary');
231
+ });
232
+ });
@@ -0,0 +1,18 @@
1
+ import React from 'react';
2
+ import { shallow } from 'enzyme';
3
+
4
+ import ErrorBoundaryFallbackComponent from '../ErrorBoundaryFallbackComponent';
5
+
6
+ describe('ErrorBoundaryFallbackComponent', function () {
7
+ var mockError = void 0;
8
+
9
+ beforeAll(function () {
10
+ mockError = new Error('You cast an unforgivable curse! You’ve earned a one-way ticket to Azkaban.');
11
+ });
12
+
13
+ it('Passes a snapshot test', function () {
14
+ var wrapper = shallow(React.createElement(ErrorBoundaryFallbackComponent, { error: mockError, componentStack: '' }));
15
+
16
+ expect(wrapper).toMatchSnapshot();
17
+ });
18
+ });
@@ -0,0 +1,213 @@
1
+ // Jest Snapshot v1, https://goo.gl/fbAQLP
2
+
3
+ exports[`ErrorBoundaryFallbackComponent Passes a snapshot test 1`] = `
4
+ ShallowWrapper {
5
+ Symbol(enzyme.__root__): [Circular],
6
+ Symbol(enzyme.__unrendered__): <ErrorBoundaryFallbackComponent
7
+ componentStack=""
8
+ error={[Error: You cast an unforgivable curse! You’ve earned a one-way ticket to Azkaban.]}
9
+ />,
10
+ Symbol(enzyme.__renderer__): Object {
11
+ "batchedUpdates": [Function],
12
+ "getNode": [Function],
13
+ "render": [Function],
14
+ "simulateEvent": [Function],
15
+ "unmount": [Function],
16
+ },
17
+ Symbol(enzyme.__node__): Object {
18
+ "instance": null,
19
+ "key": undefined,
20
+ "nodeType": "host",
21
+ "props": Object {
22
+ "children": <svg
23
+ preserveAspectRatio="xMidYMid"
24
+ style={
25
+ Object {
26
+ "fill": "currentColor",
27
+ "flex": "1 1 auto",
28
+ }
29
+ }
30
+ viewBox="0 0 24 24"
31
+ >
32
+ <path
33
+ d="M20,12A8,8 0 0,0 12,4A8,8 0 0,0 4,12A8,8 0 0,0 12,20A8,8 0 0,0 20,
34
+ 12M22,12A10,10 0 0,1 12,22A10,10 0 0,1 2,12A10,10 0 0,1 12,2A10,10 0 0,1 22,
35
+ 12M15.5,8C16.3,8 17,8.7 17,9.5C17,10.3 16.3,11 15.5,11C14.7,11 14,10.3 14,
36
+ 9.5C14,8.7 14.7,8 15.5,8M10,9.5C10,10.3 9.3,11 8.5,11C7.7,11 7,10.3 7,9.5C7,
37
+ 8.7 7.7,8 8.5,8C9.3,8 10,8.7 10,9.5M12,14C13.75,14 15.29,14.72 16.19,
38
+ 15.81L14.77,17.23C14.32,16.5 13.25,16 12,16C10.75,16 9.68,16.5 9.23,
39
+ 17.23L7.81,15.81C8.71,14.72 10.25,14 12,14Z"
40
+ />
41
+ </svg>,
42
+ "style": Object {
43
+ "alignItems": "center",
44
+ "backgroundColor": "#C00",
45
+ "boxSizing": "border-box",
46
+ "color": "#FFF",
47
+ "cursor": "help",
48
+ "display": "flex",
49
+ "flexDirection": "column",
50
+ "height": "100%",
51
+ "maxHeight": "100vh",
52
+ "maxWidth": "100vw",
53
+ "textAlign": "center",
54
+ "width": "100%",
55
+ },
56
+ "title": "Error: You cast an unforgivable curse! You’ve earned a one-way ticket to Azkaban.
57
+
58
+ This is located at:",
59
+ },
60
+ "ref": null,
61
+ "rendered": Object {
62
+ "instance": null,
63
+ "key": undefined,
64
+ "nodeType": "host",
65
+ "props": Object {
66
+ "children": <path
67
+ d="M20,12A8,8 0 0,0 12,4A8,8 0 0,0 4,12A8,8 0 0,0 12,20A8,8 0 0,0 20,
68
+ 12M22,12A10,10 0 0,1 12,22A10,10 0 0,1 2,12A10,10 0 0,1 12,2A10,10 0 0,1 22,
69
+ 12M15.5,8C16.3,8 17,8.7 17,9.5C17,10.3 16.3,11 15.5,11C14.7,11 14,10.3 14,
70
+ 9.5C14,8.7 14.7,8 15.5,8M10,9.5C10,10.3 9.3,11 8.5,11C7.7,11 7,10.3 7,9.5C7,
71
+ 8.7 7.7,8 8.5,8C9.3,8 10,8.7 10,9.5M12,14C13.75,14 15.29,14.72 16.19,
72
+ 15.81L14.77,17.23C14.32,16.5 13.25,16 12,16C10.75,16 9.68,16.5 9.23,
73
+ 17.23L7.81,15.81C8.71,14.72 10.25,14 12,14Z"
74
+ />,
75
+ "preserveAspectRatio": "xMidYMid",
76
+ "style": Object {
77
+ "fill": "currentColor",
78
+ "flex": "1 1 auto",
79
+ },
80
+ "viewBox": "0 0 24 24",
81
+ },
82
+ "ref": null,
83
+ "rendered": Object {
84
+ "instance": null,
85
+ "key": undefined,
86
+ "nodeType": "host",
87
+ "props": Object {
88
+ "d": "M20,12A8,8 0 0,0 12,4A8,8 0 0,0 4,12A8,8 0 0,0 12,20A8,8 0 0,0 20,
89
+ 12M22,12A10,10 0 0,1 12,22A10,10 0 0,1 2,12A10,10 0 0,1 12,2A10,10 0 0,1 22,
90
+ 12M15.5,8C16.3,8 17,8.7 17,9.5C17,10.3 16.3,11 15.5,11C14.7,11 14,10.3 14,
91
+ 9.5C14,8.7 14.7,8 15.5,8M10,9.5C10,10.3 9.3,11 8.5,11C7.7,11 7,10.3 7,9.5C7,
92
+ 8.7 7.7,8 8.5,8C9.3,8 10,8.7 10,9.5M12,14C13.75,14 15.29,14.72 16.19,
93
+ 15.81L14.77,17.23C14.32,16.5 13.25,16 12,16C10.75,16 9.68,16.5 9.23,
94
+ 17.23L7.81,15.81C8.71,14.72 10.25,14 12,14Z",
95
+ },
96
+ "ref": null,
97
+ "rendered": null,
98
+ "type": "path",
99
+ },
100
+ "type": "svg",
101
+ },
102
+ "type": "div",
103
+ },
104
+ Symbol(enzyme.__nodes__): Array [
105
+ Object {
106
+ "instance": null,
107
+ "key": undefined,
108
+ "nodeType": "host",
109
+ "props": Object {
110
+ "children": <svg
111
+ preserveAspectRatio="xMidYMid"
112
+ style={
113
+ Object {
114
+ "fill": "currentColor",
115
+ "flex": "1 1 auto",
116
+ }
117
+ }
118
+ viewBox="0 0 24 24"
119
+ >
120
+ <path
121
+ d="M20,12A8,8 0 0,0 12,4A8,8 0 0,0 4,12A8,8 0 0,0 12,20A8,8 0 0,0 20,
122
+ 12M22,12A10,10 0 0,1 12,22A10,10 0 0,1 2,12A10,10 0 0,1 12,2A10,10 0 0,1 22,
123
+ 12M15.5,8C16.3,8 17,8.7 17,9.5C17,10.3 16.3,11 15.5,11C14.7,11 14,10.3 14,
124
+ 9.5C14,8.7 14.7,8 15.5,8M10,9.5C10,10.3 9.3,11 8.5,11C7.7,11 7,10.3 7,9.5C7,
125
+ 8.7 7.7,8 8.5,8C9.3,8 10,8.7 10,9.5M12,14C13.75,14 15.29,14.72 16.19,
126
+ 15.81L14.77,17.23C14.32,16.5 13.25,16 12,16C10.75,16 9.68,16.5 9.23,
127
+ 17.23L7.81,15.81C8.71,14.72 10.25,14 12,14Z"
128
+ />
129
+ </svg>,
130
+ "style": Object {
131
+ "alignItems": "center",
132
+ "backgroundColor": "#C00",
133
+ "boxSizing": "border-box",
134
+ "color": "#FFF",
135
+ "cursor": "help",
136
+ "display": "flex",
137
+ "flexDirection": "column",
138
+ "height": "100%",
139
+ "maxHeight": "100vh",
140
+ "maxWidth": "100vw",
141
+ "textAlign": "center",
142
+ "width": "100%",
143
+ },
144
+ "title": "Error: You cast an unforgivable curse! You’ve earned a one-way ticket to Azkaban.
145
+
146
+ This is located at:",
147
+ },
148
+ "ref": null,
149
+ "rendered": Object {
150
+ "instance": null,
151
+ "key": undefined,
152
+ "nodeType": "host",
153
+ "props": Object {
154
+ "children": <path
155
+ d="M20,12A8,8 0 0,0 12,4A8,8 0 0,0 4,12A8,8 0 0,0 12,20A8,8 0 0,0 20,
156
+ 12M22,12A10,10 0 0,1 12,22A10,10 0 0,1 2,12A10,10 0 0,1 12,2A10,10 0 0,1 22,
157
+ 12M15.5,8C16.3,8 17,8.7 17,9.5C17,10.3 16.3,11 15.5,11C14.7,11 14,10.3 14,
158
+ 9.5C14,8.7 14.7,8 15.5,8M10,9.5C10,10.3 9.3,11 8.5,11C7.7,11 7,10.3 7,9.5C7,
159
+ 8.7 7.7,8 8.5,8C9.3,8 10,8.7 10,9.5M12,14C13.75,14 15.29,14.72 16.19,
160
+ 15.81L14.77,17.23C14.32,16.5 13.25,16 12,16C10.75,16 9.68,16.5 9.23,
161
+ 17.23L7.81,15.81C8.71,14.72 10.25,14 12,14Z"
162
+ />,
163
+ "preserveAspectRatio": "xMidYMid",
164
+ "style": Object {
165
+ "fill": "currentColor",
166
+ "flex": "1 1 auto",
167
+ },
168
+ "viewBox": "0 0 24 24",
169
+ },
170
+ "ref": null,
171
+ "rendered": Object {
172
+ "instance": null,
173
+ "key": undefined,
174
+ "nodeType": "host",
175
+ "props": Object {
176
+ "d": "M20,12A8,8 0 0,0 12,4A8,8 0 0,0 4,12A8,8 0 0,0 12,20A8,8 0 0,0 20,
177
+ 12M22,12A10,10 0 0,1 12,22A10,10 0 0,1 2,12A10,10 0 0,1 12,2A10,10 0 0,1 22,
178
+ 12M15.5,8C16.3,8 17,8.7 17,9.5C17,10.3 16.3,11 15.5,11C14.7,11 14,10.3 14,
179
+ 9.5C14,8.7 14.7,8 15.5,8M10,9.5C10,10.3 9.3,11 8.5,11C7.7,11 7,10.3 7,9.5C7,
180
+ 8.7 7.7,8 8.5,8C9.3,8 10,8.7 10,9.5M12,14C13.75,14 15.29,14.72 16.19,
181
+ 15.81L14.77,17.23C14.32,16.5 13.25,16 12,16C10.75,16 9.68,16.5 9.23,
182
+ 17.23L7.81,15.81C8.71,14.72 10.25,14 12,14Z",
183
+ },
184
+ "ref": null,
185
+ "rendered": null,
186
+ "type": "path",
187
+ },
188
+ "type": "svg",
189
+ },
190
+ "type": "div",
191
+ },
192
+ ],
193
+ Symbol(enzyme.__options__): Object {
194
+ "adapter": ReactSixteenAdapter {
195
+ "options": Object {
196
+ "enableComponentDidUpdateOnSetState": true,
197
+ "lifecycles": Object {
198
+ "componentDidUpdate": Object {
199
+ "onSetState": true,
200
+ },
201
+ "getDerivedStateFromProps": true,
202
+ "getSnapshotBeforeUpdate": true,
203
+ "setState": Object {
204
+ "skipsComponentDidUpdateOnNullish": true,
205
+ },
206
+ },
207
+ },
208
+ },
209
+ "attachTo": undefined,
210
+ "hydrateIn": undefined,
211
+ },
212
+ }
213
+ `;
@@ -0,0 +1,4 @@
1
+ import Enzyme from 'enzyme';
2
+ import Adapter from 'enzyme-adapter-react-16';
3
+
4
+ Enzyme.configure({ adapter: new Adapter() });
@@ -633,7 +633,7 @@
633
633
  return null !== error ? _react2.default.createElement(FallbackComponent, {
634
634
  componentStack: info ? info.componentStack : "",
635
635
  error: error
636
- }) : children;
636
+ }) : children || null;
637
637
  }
638
638
  } ]), ErrorBoundary;
639
639
  }(_react.Component);
@@ -641,12 +641,14 @@
641
641
  FallbackComponent: _ErrorBoundaryFallbackComponent2.default
642
642
  };
643
643
  exports.withErrorBoundary = function(Component, FallbackComponent, onError) {
644
- return function(props) {
644
+ var Wrapped = function(props) {
645
645
  return _react2.default.createElement(ErrorBoundary, {
646
646
  FallbackComponent: FallbackComponent,
647
647
  onError: onError
648
648
  }, _react2.default.createElement(Component, props));
649
- };
649
+ }, name = Component.displayName || Component.name;
650
+ return Wrapped.displayName = name ? "WithErrorBoundary(" + name + ")" : "WithErrorBoundary",
651
+ Wrapped;
650
652
  };
651
653
  exports.default = ErrorBoundary;
652
654
  }, /* 45 */
package/index.d.ts ADDED
@@ -0,0 +1,19 @@
1
+ import * as React from 'react';
2
+
3
+ export interface FallbackProps {
4
+ error?: Error;
5
+ componentStack?: string;
6
+ }
7
+
8
+ export interface ErrorBoundaryProps {
9
+ onError?: (error: Error, componentStack: string) => void;
10
+ FallbackComponent?: React.ComponentType<FallbackProps>;
11
+ }
12
+
13
+ export function withErrorBoundary<P>(
14
+ ComponentToDecorate: React.ComponentType<P>,
15
+ CustomFallbackComponent?: React.ComponentType<FallbackProps>,
16
+ onErrorHandler?: (error: Error, componentStack: string) => void,
17
+ ): React.ComponentType<P>;
18
+
19
+ export default class ErrorBoundary extends React.Component<ErrorBoundaryProps>{}
package/package.json CHANGED
@@ -1,11 +1,12 @@
1
1
  {
2
2
  "name": "react-error-boundary",
3
- "version": "1.2.1",
3
+ "version": "1.2.5",
4
4
  "description": "Sample reusable React error boundary component for React 16+",
5
5
  "homepage": "https://github.com/bvaughn/react-error-boundary",
6
6
  "repository": "bvaughn/react-error-boundary",
7
7
  "files": [
8
- "dist"
8
+ "dist",
9
+ "index.d.ts"
9
10
  ],
10
11
  "main": "dist/commonjs/index.js",
11
12
  "scripts": {
@@ -20,7 +21,7 @@
20
21
  "flow": "flow check src",
21
22
  "lint": "eslint 'src/**/*.js'",
22
23
  "prettier": "prettier --config .prettierrc --write \"src/**/*.js\"",
23
- "test": "echo \"Error: no test specified\" && exit 1"
24
+ "test": "jest"
24
25
  },
25
26
  "author": "Brian Vaughn <brian.david.vaughn@gmail.com>",
26
27
  "license": "MIT",
@@ -28,6 +29,7 @@
28
29
  "babel-cli": "^6.24.1",
29
30
  "babel-core": "^6.25.0",
30
31
  "babel-eslint": "^8.0.1",
32
+ "babel-jest": "^23.4.2",
31
33
  "babel-loader": "^7.1.1",
32
34
  "babel-plugin-flow-react-proptypes": "^3.4.2",
33
35
  "babel-plugin-transform-react-remove-prop-types": "^0.4.6",
@@ -39,6 +41,8 @@
39
41
  "babel-preset-stage-1": "^6.24.1",
40
42
  "babel-runtime": "^6.23.0",
41
43
  "cross-env": "^5.0.1",
44
+ "enzyme": "^3.5.0",
45
+ "enzyme-adapter-react-16": "^1.3.0",
42
46
  "eslint": "^4.2.0",
43
47
  "eslint-config-fbjs": "^2.0.0",
44
48
  "eslint-plugin-babel": "^4.1.2",
@@ -48,8 +52,12 @@
48
52
  "eslint-plugin-react": "^7.4.0",
49
53
  "eslint-plugin-relay": "^0.0.19",
50
54
  "flow-bin": "^0.57.3",
55
+ "jest": "^23.5.0",
51
56
  "prettier": "^1.7.4",
52
57
  "prop-types": "^15.5.10",
58
+ "react": "^16.4.2",
59
+ "react-dom": "^16.4.2",
60
+ "react-test-renderer": "^16.4.2",
53
61
  "rimraf": "^2.6.1",
54
62
  "webpack": "^3.3.0"
55
63
  },