CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
sagemathinc

Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.

GitHub Repository: sagemathinc/cocalc
Path: blob/master/src/packages/frontend/components/error-display.tsx
Views: 687
1
/*
2
* This file is part of CoCalc: Copyright © 2020 Sagemath, Inc.
3
* License: MS-RSL – see LICENSE.md for details
4
*/
5
6
import React from "react";
7
import * as misc from "@cocalc/util/misc";
8
import { Icon } from "./index";
9
import { Alert, Button } from "antd";
10
11
// use "style" to customize
12
const ELEMENT_STYLE: React.CSSProperties = {
13
overflowY: "auto",
14
} as const;
15
16
// use "body_style" prop to customize
17
const BODY_STYLE: React.CSSProperties = {
18
marginRight: "10px",
19
whiteSpace: "pre-wrap",
20
} as const;
21
22
const CLOSE_X: React.CSSProperties = {
23
float: "right",
24
position: "absolute",
25
top: "5px",
26
right: "10px",
27
zIndex: 1,
28
} as const;
29
30
interface Props {
31
error?: string | object;
32
error_component?: JSX.Element | JSX.Element[];
33
title?: string;
34
style?: React.CSSProperties;
35
body_style?: React.CSSProperties;
36
componentStyle?: React.CSSProperties;
37
bsStyle?: string;
38
onClose?: () => void;
39
banner?: boolean;
40
}
41
42
export const ErrorDisplay: React.FC<Props> = React.memo((props: Props) => {
43
const {
44
error,
45
error_component,
46
title,
47
body_style,
48
componentStyle,
49
style,
50
bsStyle,
51
onClose,
52
banner = false,
53
} = props;
54
55
function render_title() {
56
return <h4>{title}</h4>;
57
}
58
59
function render_error() {
60
if (error != undefined) {
61
if (typeof error === "string") {
62
return error;
63
} else {
64
return misc.to_json(error);
65
}
66
} else {
67
return error_component;
68
}
69
}
70
71
function type(): string {
72
if (
73
// only types that antd has...
74
bsStyle != null &&
75
["success", "info", "warning", "error"].includes(bsStyle)
76
) {
77
return bsStyle;
78
} else {
79
return "error";
80
}
81
}
82
83
function msgdesc() {
84
if (title) {
85
return [
86
render_title(),
87
<div style={{ ...BODY_STYLE, ...body_style }}>{render_error()}</div>,
88
];
89
} else {
90
return [
91
<div style={{ ...BODY_STYLE, ...body_style }}>{render_error()}</div>,
92
undefined,
93
];
94
}
95
}
96
97
// must be rendered as the first child element!
98
function render_close() {
99
if (onClose == null || banner === false) return;
100
return (
101
<Button
102
style={CLOSE_X}
103
shape="circle"
104
size="small"
105
type="text"
106
onClick={onClose}
107
>
108
<Icon style={style} name="times" />
109
</Button>
110
);
111
}
112
113
function render_alert() {
114
const [message, description] = msgdesc();
115
// tweak the case where it's not a banner
116
const extra = banner ? undefined : { closable: true, onClose };
117
return (
118
<Alert
119
banner={banner}
120
showIcon={false}
121
style={{ ...ELEMENT_STYLE, ...style }}
122
type={type() as any}
123
message={message}
124
description={description}
125
{...extra}
126
/>
127
);
128
}
129
130
const divprops = banner ? { className: "cc-error-display" } : undefined;
131
132
return (
133
<div {...divprops} style={componentStyle}>
134
{render_close()}
135
{render_alert()}
136
</div>
137
);
138
});
139
140