From f7298411621a9c02b192e9d761b567f04bac14c6 Mon Sep 17 00:00:00 2001 From: Joe Date: Tue, 22 Oct 2024 09:49:19 -0500 Subject: [PATCH] feat: Add render blocking errors to Chart (#2255) Adds new events that enable render-blocking overlays to the chart. The current error pop-up is not sufficient because it still allows the chart to render. This is problematic in the case of webgl because disabling webgl means we don't want the chart rendering at all in case the computer can't handle it. --- packages/chart/src/Chart.tsx | 78 +++++++++++++++++++++++--------- packages/chart/src/ChartModel.ts | 12 +++++ 2 files changed, 68 insertions(+), 22 deletions(-) diff --git a/packages/chart/src/Chart.tsx b/packages/chart/src/Chart.tsx index 2b389233ed..849af4b550 100644 --- a/packages/chart/src/Chart.tsx +++ b/packages/chart/src/Chart.tsx @@ -82,11 +82,14 @@ interface ChartState { isDownsampleInProgress: boolean; isDownsamplingDisabled: boolean; - /** Any other kind of error */ + /** Any other kind of error that doesn't completely block the chart from rendering */ error: unknown; shownError: string | null; layout: Partial; revision: number; + + /** A message that blocks the chart from rendering. It can be bypassed by the user to continue rendering. */ + shownBlocker: string | null; } class Chart extends Component { @@ -180,6 +183,7 @@ class Chart extends Component { datarevision: 0, }, revision: 0, + shownBlocker: null, }; } @@ -512,6 +516,15 @@ class Chart extends Component { onError(new Error(error)); break; } + case ChartModel.EVENT_BLOCKER: { + const blocker = `${detail}`; + this.setState({ shownBlocker: blocker }); + break; + } + case ChartModel.EVENT_BLOCKER_CLEAR: { + this.setState({ shownBlocker: null }); + break; + } default: log.debug('Unknown event type', type, event); } @@ -705,6 +718,7 @@ class Chart extends Component { shownError, layout, revision, + shownBlocker, } = this.state; const config = this.getCachedConfig( downsamplingError, @@ -714,7 +728,46 @@ class Chart extends Component { data ?? [], error ); - const isPlotShown = data != null; + const { model } = this.props; + const isPlotShown = data != null && shownBlocker == null; + + let errorOverlay: React.ReactNode = null; + if (shownBlocker != null) { + errorOverlay = ( + { + model.fireBlockerClear(); + }} + /> + ); + } else if (shownError != null) { + errorOverlay = ( + { + this.handleDownsampleErrorClose(); + }} + onConfirm={() => { + this.handleDownsampleErrorClose(); + this.handleDownsampleClick(); + }} + /> + ); + } else if (downsamplingError != null) { + errorOverlay = ( + { + this.handleDownsampleErrorClose(); + }} + onConfirm={() => { + this.handleDownsampleErrorClose(); + this.handleDownsampleClick(); + }} + /> + ); + } return (
@@ -735,26 +788,7 @@ class Chart extends Component { style={{ height: '100%', width: '100%' }} /> )} - {downsamplingError != null && shownError == null && ( - { - this.handleDownsampleErrorClose(); - }} - onConfirm={() => { - this.handleDownsampleErrorClose(); - this.handleDownsampleClick(); - }} - /> - )} - {shownError != null && ( - { - this.handleErrorClose(); - }} - /> - )} + {errorOverlay}
); } diff --git a/packages/chart/src/ChartModel.ts b/packages/chart/src/ChartModel.ts index 0cf03815af..ad03608766 100644 --- a/packages/chart/src/ChartModel.ts +++ b/packages/chart/src/ChartModel.ts @@ -37,6 +37,10 @@ class ChartModel { static EVENT_ERROR = 'ChartModel.EVENT_ERROR'; + static EVENT_BLOCKER = 'ChartModel.EVENT_BLOCKER'; + + static EVENT_BLOCKER_CLEAR = 'ChartModel.EVENT_BLOCKER_CLEAR'; + constructor(dh: typeof DhType) { this.dh = dh; this.listeners = []; @@ -184,6 +188,14 @@ class ChartModel { fireError(detail: string[]): void { this.fireEvent(new CustomEvent(ChartModel.EVENT_ERROR, { detail })); } + + fireBlocker(detail: string[]): void { + this.fireEvent(new CustomEvent(ChartModel.EVENT_BLOCKER, { detail })); + } + + fireBlockerClear(): void { + this.fireEvent(new CustomEvent(ChartModel.EVENT_BLOCKER_CLEAR)); + } } export default ChartModel;