11import React , { Component , ReactElement , RefObject } from 'react' ;
22import deepEqual from 'deep-equal' ;
33import memoize from 'memoize-one' ;
4+ import { CopyButton , Popper } from '@deephaven/components' ;
45import {
56 vsLoading ,
67 dhGraphLineDown ,
@@ -33,6 +34,7 @@ import Plotly from './plotly/Plotly';
3334import ChartModel from './ChartModel' ;
3435import ChartUtils , { ChartModelSettings } from './ChartUtils' ;
3536import './Chart.scss' ;
37+ import DownsamplingError from './DownsamplingError' ;
3638
3739const log = Log . module ( 'Chart' ) ;
3840
@@ -56,10 +58,15 @@ interface ChartProps {
5658
5759interface ChartState {
5860 data : Partial < Data > [ ] | null ;
61+ /** An error specific to downsampling */
5962 downsamplingError : unknown ;
6063 isDownsampleFinished : boolean ;
6164 isDownsampleInProgress : boolean ;
6265 isDownsamplingDisabled : boolean ;
66+
67+ /** Any other kind of error */
68+ error : unknown ;
69+ shownError : string | null ;
6370 layout : Partial < Layout > ;
6471 revision : number ;
6572}
@@ -129,6 +136,7 @@ export class Chart extends Component<ChartProps, ChartState> {
129136
130137 this . handleAfterPlot = this . handleAfterPlot . bind ( this ) ;
131138 this . handleDownsampleClick = this . handleDownsampleClick . bind ( this ) ;
139+ this . handleErrorClose = this . handleErrorClose . bind ( this ) ;
132140 this . handleModelEvent = this . handleModelEvent . bind ( this ) ;
133141 this . handlePlotUpdate = this . handlePlotUpdate . bind ( this ) ;
134142 this . handleRelayout = this . handleRelayout . bind ( this ) ;
@@ -153,6 +161,8 @@ export class Chart extends Component<ChartProps, ChartState> {
153161 isDownsampleFinished : false ,
154162 isDownsampleInProgress : false ,
155163 isDownsamplingDisabled : false ,
164+ error : null ,
165+ shownError : null ,
156166 layout : {
157167 datarevision : 0 ,
158168 } ,
@@ -236,15 +246,30 @@ export class Chart extends Component<ChartProps, ChartState> {
236246 isDownsampleFinished : boolean ,
237247 isDownsampleInProgress : boolean ,
238248 isDownsamplingDisabled : boolean ,
239- data : Partial < Data > [ ]
249+ data : Partial < Data > [ ] ,
250+ error : unknown
240251 ) : Partial < PlotlyConfig > => {
241252 const customButtons : ModeBarButtonAny [ ] = [ ] ;
242253 const hasDownsampleError = Boolean ( downsamplingError ) ;
243254 if ( hasDownsampleError ) {
244255 customButtons . push ( {
245256 name : `Downsampling failed: ${ downsamplingError } ` ,
246257 title : 'Downsampling failed' ,
247- click : ( ) => undefined ,
258+ click : ( ) => {
259+ this . toggleErrorMessage ( `${ downsamplingError } ` ) ;
260+ } ,
261+ icon : Chart . convertIcon ( dhWarningFilled ) ,
262+ attr : 'fill-warning' ,
263+ } ) ;
264+ }
265+ const hasError = Boolean ( error ) ;
266+ if ( hasError ) {
267+ customButtons . push ( {
268+ name : `Error: ${ error } ` ,
269+ title : `Error` ,
270+ click : ( ) => {
271+ this . toggleErrorMessage ( `${ error } ` ) ;
272+ } ,
248273 icon : Chart . convertIcon ( dhWarningFilled ) ,
249274 attr : 'fill-warning' ,
250275 } ) ;
@@ -301,7 +326,7 @@ export class Chart extends Component<ChartProps, ChartState> {
301326 // Yes, the value is a boolean or the string 'hover': https://github.com/plotly/plotly.js/blob/master/src/plot_api/plot_config.js#L249
302327 displayModeBar :
303328 // eslint-disable-next-line @typescript-eslint/strict-boolean-expressions
304- isDownsampleInProgress || hasDownsampleError
329+ isDownsampleInProgress || hasDownsampleError || hasError
305330 ? true
306331 : ( 'hover' as const ) ,
307332
@@ -376,6 +401,10 @@ export class Chart extends Component<ChartProps, ChartState> {
376401 ) ;
377402 }
378403
404+ handleErrorClose ( ) : void {
405+ this . setState ( { shownError : null } ) ;
406+ }
407+
379408 handleModelEvent ( event : CustomEvent ) : void {
380409 const { type, detail } = event ;
381410 log . debug2 ( 'Received data update' , type , detail ) ;
@@ -442,7 +471,14 @@ export class Chart extends Component<ChartProps, ChartState> {
442471 } ) ;
443472
444473 const { onError } = this . props ;
445- onError ( new Error ( downsamplingError ) ) ;
474+ onError ( new DownsamplingError ( downsamplingError ) ) ;
475+ break ;
476+ }
477+ case ChartModel . EVENT_ERROR : {
478+ const error = `${ detail } ` ;
479+ this . setState ( { error } ) ;
480+ const { onError } = this . props ;
481+ onError ( new Error ( error ) ) ;
446482 break ;
447483 }
448484 default :
@@ -502,6 +538,15 @@ export class Chart extends Component<ChartProps, ChartState> {
502538 }
503539 }
504540
541+ /**
542+ * Toggle the error message. If it is already being displayed, then hide it.
543+ */
544+ toggleErrorMessage ( error : string ) : void {
545+ this . setState ( ( { shownError } ) => ( {
546+ shownError : shownError === error ? null : error ,
547+ } ) ) ;
548+ }
549+
505550 /**
506551 * Update the models dimensions and ranges.
507552 * Note that this will update it all whether the plot size changes OR the range
@@ -601,6 +646,8 @@ export class Chart extends Component<ChartProps, ChartState> {
601646 isDownsampleFinished,
602647 isDownsampleInProgress,
603648 isDownsamplingDisabled,
649+ error,
650+ shownError,
604651 layout,
605652 revision,
606653 } = this . state ;
@@ -609,7 +656,8 @@ export class Chart extends Component<ChartProps, ChartState> {
609656 isDownsampleFinished ,
610657 isDownsampleInProgress ,
611658 isDownsamplingDisabled ,
612- data ?? [ ]
659+ data ?? [ ] ,
660+ error
613661 ) ;
614662 const isPlotShown = data != null ;
615663 return (
@@ -632,6 +680,23 @@ export class Chart extends Component<ChartProps, ChartState> {
632680 style = { { height : '100%' , width : '100%' } }
633681 />
634682 ) }
683+ < Popper
684+ className = "chart-error-popper"
685+ options = { { placement : 'top' } }
686+ isShown = { shownError != null }
687+ onExited = { this . handleErrorClose }
688+ closeOnBlur
689+ interactive
690+ >
691+ { shownError != null && (
692+ < >
693+ < div className = "chart-error" > { shownError } </ div >
694+ < CopyButton tooltip = "Copy Error" copy = { shownError } >
695+ Copy Error
696+ </ CopyButton >
697+ </ >
698+ ) }
699+ </ Popper >
635700 </ div >
636701 ) ;
637702 }
0 commit comments