Skip to content

Commit 2016391

Browse files
committed
[ERP-3472] Expose error details and provide 'copy to clipboard' functionality to allow us to reconcile to the user session in Splunk RUM
1 parent ee35d6f commit 2016391

File tree

4 files changed

+68
-7
lines changed

4 files changed

+68
-7
lines changed

frontend/.eslintrc.json

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,12 @@
4747
"@typescript-eslint/explicit-module-boundary-types": "off",
4848
"@typescript-eslint/no-unused-vars": [
4949
"error",
50-
{ "ignoreRestSiblings": true }
50+
{
51+
"ignoreRestSiblings": true,
52+
"caughtErrorsIgnorePattern": "^_",
53+
"destructuredArrayIgnorePattern": "^_",
54+
"varsIgnorePattern": "^_"
55+
}
5156
],
5257
"@typescript-eslint/no-explicit-any": "off",
5358
"@typescript-eslint/no-var-requires": "off",

frontend/components/errorBoundary/asyncErrorBoundary.stories.tsx

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,13 @@ const ErrorComponent = () => {
1717
throw new Error("Test error");
1818
};
1919

20+
const PromiseRejectComponent = () => {
21+
new Promise((_, reject) => {
22+
reject(new Error("Unexpected promise rejection"));
23+
}).then();
24+
return <></>;
25+
};
26+
2027
export const Default: Story = {
2128
args: {
2229
children: <ErrorComponent />,
@@ -28,6 +35,13 @@ export const Default: Story = {
2835
),
2936
};
3037

38+
export const WithPromiseRejection: Story = {
39+
...Default,
40+
args: {
41+
children: <PromiseRejectComponent />,
42+
},
43+
};
44+
3145
export const WithNoErrors: Story = {
3246
...Default,
3347
args: {

frontend/components/errorMessage/errorMessage.stories.tsx

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,6 @@ const ErrorComponent = () => {
1414
};
1515

1616
export const Default: Story = {
17-
args: {
18-
message: "An error occurred while processing your request.",
19-
},
2017
render: (args) => (
2118
<ErrorBoundary fallback={<ErrorMessage {...args} />}>
2219
<ErrorComponent />

frontend/components/errorMessage/errorMessage.tsx

Lines changed: 48 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { FunctionComponent } from "react";
1+
import { FunctionComponent, useEffect, useState } from "react";
22
import {
33
DialogBackdrop,
44
DialogBody,
@@ -10,23 +10,46 @@ import {
1010
DialogTitle,
1111
DialogTrigger,
1212
} from "@/components/ui/dialog";
13-
import { Text } from "@chakra-ui/react";
13+
import { Box, Button, Clipboard, Text } from "@chakra-ui/react";
1414
import { ExternalLink } from "@/components/externalLink";
1515
import { FallbackProps, useErrorBoundary } from "react-error-boundary";
1616
import { MappedIcon } from "@/components/mappedIcon";
17+
import SplunkOtelWeb from "@splunk/otel-web";
18+
import { capitalize, isEmpty, words } from "lodash";
1719

1820
export interface ErrorMessageProps extends FallbackProps {
1921
icon?: string;
2022
title?: string;
2123
message?: string;
2224
}
2325

26+
type ErrorDetail = { label: string; value: string };
27+
const clipboardText = (detail: Array<ErrorDetail>) =>
28+
detail.map(({ label, value }) => `${label}: ${value}`).join("\n");
29+
2430
const ErrorMessage: FunctionComponent<ErrorMessageProps & FallbackProps> = ({
2531
icon = "exclamation-circle",
2632
title = "An unexpected error has occurred",
2733
message,
34+
error,
2835
}) => {
2936
const { resetBoundary } = useErrorBoundary();
37+
const [errorDetails, setErrorDetails] = useState<Array<ErrorDetail>>([]);
38+
39+
useEffect(() => {
40+
setErrorDetails(
41+
Object.entries({
42+
splunkSessionId: SplunkOtelWeb.getSessionId(),
43+
timestamp: new Date().toISOString(),
44+
detail:
45+
error?.reason?.toString() ?? error?.message ?? error?.toString(),
46+
})
47+
.filter(([_key, value]) => !isEmpty(value))
48+
.map(([key, value]) => {
49+
return { label: words(key)?.map(capitalize)?.join(" "), value };
50+
}),
51+
);
52+
}, [error]);
3053

3154
return (
3255
<DialogRoot
@@ -56,8 +79,30 @@ const ErrorMessage: FunctionComponent<ErrorMessageProps & FallbackProps> = ({
5679
>
5780
contact eResearch
5881
</ExternalLink>{" "}
59-
for assistance.
82+
for assistance, providing the following information:
6083
</Text>
84+
<Box
85+
border={"dashed"}
86+
borderWidth={1}
87+
borderColor={"gray.300"}
88+
p={2}
89+
mt={4}
90+
mb={4}
91+
>
92+
{errorDetails.map(({ label, value }) => (
93+
<Text key={label}>
94+
{label}: {value}
95+
</Text>
96+
))}
97+
</Box>
98+
<Clipboard.Root value={clipboardText(errorDetails)}>
99+
<Clipboard.Trigger asChild>
100+
<Button variant="surface" size="sm">
101+
<Clipboard.Indicator />
102+
<Clipboard.CopyText />
103+
</Button>
104+
</Clipboard.Trigger>
105+
</Clipboard.Root>
61106
</DialogBody>
62107
<DialogFooter />
63108
</DialogContent>

0 commit comments

Comments
 (0)