Skip to content

Commit e3e36c7

Browse files
committed
Use internal component for displaying an error if data loading fails
1 parent d6689de commit e3e36c7

8 files changed

Lines changed: 154 additions & 28 deletions

File tree

packages/web-components/src/components.d.ts

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -318,6 +318,13 @@ export namespace Components {
318318
*/
319319
"subjects"?: string;
320320
}
321+
/**
322+
* A component that displays an error message when GO data fails to load.
323+
*/
324+
interface GoDataLoadError {
325+
"componentName": string;
326+
"error": Error;
327+
}
321328
/**
322329
* The Entity Autocomplete component provides an input field that allows users to search for
323330
* entities (genes or GO terms) using the GO API.
@@ -515,6 +522,15 @@ declare global {
515522
prototype: HTMLGoAnnotationRibbonTableElement;
516523
new (): HTMLGoAnnotationRibbonTableElement;
517524
};
525+
/**
526+
* A component that displays an error message when GO data fails to load.
527+
*/
528+
interface HTMLGoDataLoadErrorElement extends Components.GoDataLoadError, HTMLStencilElement {
529+
}
530+
var HTMLGoDataLoadErrorElement: {
531+
prototype: HTMLGoDataLoadErrorElement;
532+
new (): HTMLGoDataLoadErrorElement;
533+
};
518534
interface HTMLGoEntityAutocompleteElementEventMap {
519535
"itemSelected": any;
520536
}
@@ -618,6 +634,7 @@ declare global {
618634
"go-annotation-ribbon-strips": HTMLGoAnnotationRibbonStripsElement;
619635
"go-annotation-ribbon-subject": HTMLGoAnnotationRibbonSubjectElement;
620636
"go-annotation-ribbon-table": HTMLGoAnnotationRibbonTableElement;
637+
"go-data-load-error": HTMLGoDataLoadErrorElement;
621638
"go-entity-autocomplete": HTMLGoEntityAutocompleteElement;
622639
"go-gocam-viewer": HTMLGoGocamViewerElement;
623640
"go-gocam-viewer-legend": HTMLGoGocamViewerLegendElement;
@@ -953,6 +970,13 @@ declare namespace LocalJSX {
953970
*/
954971
"subjects"?: string;
955972
}
973+
/**
974+
* A component that displays an error message when GO data fails to load.
975+
*/
976+
interface GoDataLoadError {
977+
"componentName"?: string;
978+
"error"?: Error;
979+
}
956980
/**
957981
* The Entity Autocomplete component provides an input field that allows users to search for
958982
* entities (genes or GO terms) using the GO API.
@@ -1048,6 +1072,7 @@ declare namespace LocalJSX {
10481072
"go-annotation-ribbon-strips": GoAnnotationRibbonStrips;
10491073
"go-annotation-ribbon-subject": GoAnnotationRibbonSubject;
10501074
"go-annotation-ribbon-table": GoAnnotationRibbonTable;
1075+
"go-data-load-error": GoDataLoadError;
10511076
"go-entity-autocomplete": GoEntityAutocomplete;
10521077
"go-gocam-viewer": GoGocamViewer;
10531078
"go-gocam-viewer-legend": GoGocamViewerLegend;
@@ -1091,6 +1116,10 @@ declare module "@stencil/core" {
10911116
* fetch data by itself, it expects the data to be provided in the `data` attribute.
10921117
*/
10931118
"go-annotation-ribbon-table": LocalJSX.GoAnnotationRibbonTable & JSXBase.HTMLAttributes<HTMLGoAnnotationRibbonTableElement>;
1119+
/**
1120+
* A component that displays an error message when GO data fails to load.
1121+
*/
1122+
"go-data-load-error": LocalJSX.GoDataLoadError & JSXBase.HTMLAttributes<HTMLGoDataLoadErrorElement>;
10941123
/**
10951124
* The Entity Autocomplete component provides an input field that allows users to search for
10961125
* entities (genes or GO terms) using the GO API.

packages/web-components/src/components/annotation-ribbon/annotation-ribbon.scss

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -197,7 +197,3 @@ go-spinner {
197197
font-style: italic;
198198
color: #7b7b7b;
199199
}
200-
201-
.error {
202-
color: darkred;
203-
}

packages/web-components/src/components/annotation-ribbon/annotation-ribbon.tsx

Lines changed: 17 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -40,10 +40,10 @@ export class AnnotationRibbon {
4040

4141
@State() ribbonData?: RibbonData;
4242
@State() ribbonDataLoading = false;
43-
@State() ribbonDataLoadingError = false;
43+
@State() ribbonDataLoadingError: Error | undefined = undefined;
4444

4545
@State() tableDataLoading = false;
46-
@State() tableDataLoadingError = false;
46+
@State() tableDataLoadingError: Error | undefined = undefined;
4747

4848
/**
4949
* URL for the API endpoint to fetch the ribbon data when subjects are provided.
@@ -206,7 +206,7 @@ export class AnnotationRibbon {
206206
if (!this.subjects) {
207207
this.ribbonData = undefined;
208208
this.ribbonDataLoading = false;
209-
this.ribbonDataLoadingError = false;
209+
this.ribbonDataLoadingError = undefined;
210210
void this.stripsElement.setData(this.ribbonData);
211211
return void this.tableElement.setData(undefined);
212212
}
@@ -224,16 +224,15 @@ export class AnnotationRibbon {
224224

225225
try {
226226
this.ribbonDataLoading = true;
227-
this.ribbonDataLoadingError = false;
227+
this.ribbonDataLoadingError = undefined;
228228
this.ribbonData = await getRibbonSummary(
229229
this.ribbonDataApiEndpoint,
230230
this.subjects,
231231
this.subset,
232232
);
233233
return this.stripsElement.setData(this.ribbonData);
234-
} catch (error) {
235-
console.error("Error loading ribbon data:", error);
236-
this.ribbonDataLoadingError = true;
234+
} catch (error: unknown) {
235+
this.ribbonDataLoadingError = error as Error;
237236
} finally {
238237
this.ribbonDataLoading = false;
239238
}
@@ -253,7 +252,7 @@ export class AnnotationRibbon {
253252
try {
254253
await this.tableElement.setData(undefined);
255254
this.tableDataLoading = true;
256-
this.tableDataLoadingError = false;
255+
this.tableDataLoadingError = undefined;
257256
const tableData = await getTableData(
258257
this.tableDataApiEndpoint,
259258
subjectIds,
@@ -283,9 +282,8 @@ export class AnnotationRibbon {
283282
);
284283
}
285284
await this.tableElement.setData(tableData);
286-
} catch (error) {
287-
console.error("Error loading table data:", error);
288-
this.tableDataLoadingError = true;
285+
} catch (error: unknown) {
286+
this.tableDataLoadingError = error as Error;
289287
await this.tableElement.setData(undefined);
290288
} finally {
291289
this.tableDataLoading = false;
@@ -364,7 +362,10 @@ export class AnnotationRibbon {
364362
/>
365363
{this.ribbonDataLoading && <go-spinner />}
366364
{this.ribbonDataLoadingError && (
367-
<div class="error">Error loading ribbon data.</div>
365+
<go-data-load-error
366+
componentName={this.constructor.name}
367+
error={this.ribbonDataLoadingError}
368+
/>
368369
)}
369370

370371
{this.ribbonData && (
@@ -385,7 +386,10 @@ export class AnnotationRibbon {
385386
excludeProteinBinding={this.excludeProteinBinding}
386387
/>
387388
{this.tableDataLoadingError && (
388-
<div class="error">Error loading table data.</div>
389+
<go-data-load-error
390+
componentName={this.constructor.name}
391+
error={this.tableDataLoadingError}
392+
/>
389393
)}
390394
</Host>
391395
);
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
$background: #fff5f5;
2+
$border: #dbbdbd;
3+
$text: #721c24;
4+
5+
.error {
6+
border: 1px solid $border;
7+
border-radius: 4px;
8+
background: $background;
9+
color: $text;
10+
padding: 1em;
11+
12+
.error-header {
13+
font-weight: bold;
14+
padding-bottom: 1em;
15+
border-bottom: 1px solid $border;
16+
}
17+
18+
p {
19+
margin: 1em 0;
20+
}
21+
22+
pre {
23+
margin: 0;
24+
}
25+
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import { Component, h, Prop } from "@stencil/core";
2+
import { HTTPError, TimeoutError } from "ky";
3+
4+
/**
5+
* A component that displays an error message when GO data fails to load.
6+
*
7+
* @internal
8+
*/
9+
@Component({
10+
tag: "go-data-load-error",
11+
styleUrl: "data-load-error.scss",
12+
shadow: true,
13+
})
14+
export class DataLoadError {
15+
@Prop() componentName: string;
16+
@Prop() error: Error;
17+
18+
render() {
19+
let details = `Host page: ${window.location.href}
20+
Component: ${this.componentName}`;
21+
if (this.error instanceof HTTPError || this.error instanceof TimeoutError) {
22+
details += `
23+
API request: ${this.error.request.method} ${this.error.request.url}`;
24+
}
25+
return (
26+
<div class="error">
27+
<div class="error-header">
28+
<b>Failed to load GO data</b>
29+
</div>
30+
<div>
31+
<p>
32+
Please try again later. If the issue persists visit the{" "}
33+
<a
34+
href="https://help.geneontology.org/"
35+
target="_blank"
36+
rel="noopener noreferrer"
37+
>
38+
GO Helpdesk
39+
</a>{" "}
40+
to report the problem. Include the following information in your
41+
report:
42+
</p>
43+
<pre>{details}</pre>
44+
</div>
45+
</div>
46+
);
47+
}
48+
}

packages/web-components/src/components/gocam-viewer/gocam-viewer.scss

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,8 @@
7777
* @prop --node-align-items: Align-items of related node info
7878
*
7979
* @prop --function-nodes-padding: Padding of related nodes container in processes and activities panel
80+
*
81+
* @prop --loading-spinner-size: Size of loading spinner shown while loading GO-CAM data
8082
*/
8183
--border-color: rgba(0, 0, 0, 0.125);
8284

@@ -143,6 +145,8 @@
143145
@include standard-var-declarations(node, $border-color: var(--border-color));
144146

145147
--function-nodes-padding: 0;
148+
149+
--loading-spinner-size: 2em;
146150
}
147151

148152
.gocam-graph-and-activities-container {
@@ -206,6 +210,9 @@ button {
206210
height: var(--graph-height);
207211
box-sizing: border-box;
208212
position: relative;
213+
display: flex;
214+
align-items: center;
215+
justify-content: center;
209216
}
210217

211218
go-gocam-viewer-legend {
@@ -227,3 +234,7 @@ go-gocam-viewer-sidebar {
227234
go-info-popover {
228235
padding: 0 0.6em;
229236
}
237+
238+
go-spinner {
239+
--size: var(--loading-spinner-size);
240+
}

packages/web-components/src/components/gocam-viewer/gocam-viewer.tsx

Lines changed: 23 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ export class GocamViewer {
8787
/**
8888
* Indicates if the component has encountered an error while loading some data
8989
*/
90-
@State() error: boolean = false;
90+
@State() error: Error | undefined = undefined;
9191

9292
configService = new NoctuaFormConfigService();
9393

@@ -293,14 +293,14 @@ export class GocamViewer {
293293
/**
294294
* Will request the gocam from the bbop manager; if manager approves, will trigger renderGoCam
295295
*/
296-
loadGoCam() {
296+
async loadGoCam() {
297297
if (!this.gocamId || !this.apiUrl) {
298298
return;
299299
}
300300

301301
this.graphDiv.innerHTML = "";
302302
this.loading = true;
303-
this.error = false;
303+
this.error = undefined;
304304
this.cam = undefined;
305305

306306
let gocamCurie = this.gocamId;
@@ -309,14 +309,17 @@ export class GocamViewer {
309309
}
310310
const url = this.apiUrl.replace("%ID", gocamCurie);
311311

312-
ky.get(url)
313-
.json()
314-
.then((graph: any) => {
315-
const model = graph.activeModel ?? graph;
316-
if (model) {
317-
this.setModelData(model);
318-
}
319-
});
312+
try {
313+
const graph: any = await ky.get(url).json();
314+
const model = graph.activeModel ?? graph;
315+
if (model) {
316+
this.setModelData(model);
317+
}
318+
} catch (error: unknown) {
319+
this.error = error as Error;
320+
} finally {
321+
this.loading = false;
322+
}
320323
}
321324

322325
renderGoCam(cam: Cam, expandComplex = false, layout = "dagre") {
@@ -759,6 +762,15 @@ export class GocamViewer {
759762
this.genesPanel.parentCy = this.cy;
760763
}
761764

765+
if (this.error) {
766+
return (
767+
<go-data-load-error
768+
componentName={this.constructor.name}
769+
error={this.error}
770+
/>
771+
);
772+
}
773+
762774
return (
763775
<Host>
764776
<div class="gocam-graph-and-activities-container">

website/docs/components/gocam-viewer/readme.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,7 @@ Type: `Promise<void>`
144144
| `--legend-header-padding` | Padding of legend header |
145145
| `--legend-margin` | Margin of legend container |
146146
| `--legend-padding` | Padding of legend container |
147+
| `--loading-spinner-size` | Size of loading spinner shown while loading GO-CAM data |
147148
| `--node-align-items` | Align-items of related node info |
148149
| `--node-background` | Background of related node info in processes and activities panel |
149150
| `--node-border-color` | Border color of related node info in processes and activities panel |

0 commit comments

Comments
 (0)