Skip to content

Commit dfaf48d

Browse files
shakyShaneShane Osbourne
andauthored
ntp: per-widget error boundary (#1249)
Co-authored-by: Shane Osbourne <[email protected]>
1 parent ae441a9 commit dfaf48d

File tree

3 files changed

+46
-13
lines changed

3 files changed

+46
-13
lines changed

special-pages/pages/new-tab/app/index.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -170,7 +170,8 @@ async function resolveEntryPoints(widgets, didCatch) {
170170
* @param {Record<string, any>} strings
171171
*/
172172
function renderComponents(root, environment, settings, strings) {
173-
render(
173+
// eslint-disable-next-line no-labels,no-unused-labels
174+
$INTEGRATION: render(
174175
<EnvironmentProvider debugState={environment.debugState} injectName={environment.injectName} willThrow={environment.willThrow}>
175176
<SettingsProvider settings={settings}>
176177
<TranslationProvider translationObject={strings} fallback={strings} textLength={environment.textLength}>

special-pages/pages/new-tab/app/widget-list/WidgetList.js

Lines changed: 41 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
1-
import { Fragment, h } from 'preact';
1+
import { h } from 'preact';
22
import { WidgetConfigContext, WidgetVisibilityProvider } from './widget-config.provider.js';
33
import { useContext } from 'preact/hooks';
4-
import { Customizer, CustomizerMenuPositionedFixed } from '../customizer/components/Customizer';
5-
import { useEnv } from '../../../../shared/components/EnvironmentProvider.js';
6-
import { DebugCustomized } from '../telemetry/Debug.js';
4+
import { Customizer, CustomizerMenuPositionedFixed } from '../customizer/components/Customizer.js';
5+
import { useMessaging } from '../types.js';
6+
import { ErrorBoundary } from '../../../../shared/components/ErrorBoundary.js';
7+
import { Fallback } from '../../../../shared/components/Fallback/Fallback.jsx';
8+
import { Centered } from '../components/Layout.js';
79

810
/**
911
* @param {string} id
@@ -37,25 +39,53 @@ export async function widgetEntryPoint(id) {
3739

3840
export function WidgetList() {
3941
const { widgets, widgetConfigItems, entryPoints } = useContext(WidgetConfigContext);
40-
const { env } = useEnv();
42+
const messaging = useMessaging();
43+
44+
/**
45+
* @param {any} error
46+
* @param {string} id
47+
*/
48+
const didCatch = (error, id) => {
49+
const message = error?.message || error?.error || 'unknown';
50+
const composed = `Widget '${id}' threw an exception: ` + message;
51+
messaging.reportPageException({ message: composed });
52+
};
4153

4254
return (
4355
<div>
4456
{widgets.map((widget, index) => {
4557
const matchingConfig = widgetConfigItems.find((item) => item.id === widget.id);
4658
const matchingEntryPoint = entryPoints[widget.id];
59+
/**
60+
* If there's no config, it means the user does not control the visibility of the elements in question.
61+
*/
4762
if (!matchingConfig) {
48-
return <Fragment key={widget.id}>{matchingEntryPoint.factory?.()}</Fragment>;
63+
return (
64+
<ErrorBoundary key={widget.id} didCatch={(error) => didCatch(error, widget.id)} fallback={null}>
65+
{matchingEntryPoint.factory?.()}
66+
</ErrorBoundary>
67+
);
4968
}
69+
70+
/**
71+
* This section is for elements that the user controls the visibility of
72+
*/
5073
return (
51-
<Fragment key={widget.id}>
52-
<WidgetVisibilityProvider visibility={matchingConfig.visibility} id={matchingConfig.id} index={index}>
74+
<WidgetVisibilityProvider key={widget.id} visibility={matchingConfig.visibility} id={matchingConfig.id} index={index}>
75+
<ErrorBoundary
76+
key={widget.id}
77+
didCatch={(error) => didCatch(error, widget.id)}
78+
fallback={
79+
<Centered>
80+
<Fallback showDetails={true}>Widget id: {matchingConfig.id}</Fallback>
81+
</Centered>
82+
}
83+
>
5384
{matchingEntryPoint.factory?.()}
54-
</WidgetVisibilityProvider>
55-
</Fragment>
85+
</ErrorBoundary>
86+
</WidgetVisibilityProvider>
5687
);
5788
})}
58-
{env === 'development' && <DebugCustomized index={widgets.length} />}
5989
<CustomizerMenuPositionedFixed>
6090
<Customizer />
6191
</CustomizerMenuPositionedFixed>

special-pages/shared/components/Fallback/Fallback.jsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,14 @@ import styles from './Fallback.module.css';
44
/**
55
* @param {object} props
66
* @param {boolean} props.showDetails
7+
* @param {import("preact").ComponentChild} [props.children]
78
*/
8-
export function Fallback({ showDetails }) {
9+
export function Fallback({ showDetails, children }) {
910
return (
1011
<div class={styles.fallback}>
1112
<div>
1213
<p>Something went wrong!</p>
14+
{children}
1315
{showDetails && (
1416
<p>
1517
Please check logs for a message called <code>reportPageException</code>

0 commit comments

Comments
 (0)