diff --git a/pkg/ui/workspaces/cluster-ui/src/loading/loading.spec.tsx b/pkg/ui/workspaces/cluster-ui/src/loading/loading.spec.tsx index b83240f802dd..25194c9aa602 100644 --- a/pkg/ui/workspaces/cluster-ui/src/loading/loading.spec.tsx +++ b/pkg/ui/workspaces/cluster-ui/src/loading/loading.spec.tsx @@ -15,6 +15,7 @@ import { Spinner, InlineAlert } from "@cockroachlabs/ui-components"; import { Loading } from "./loading"; const SomeComponent = () =>
Hello, world!
; +const SomeCustomErrorComponent = () =>
Custom Error
; describe("", () => { describe("when error is null", () => { @@ -73,8 +74,23 @@ describe("", () => { ); assert.isFalse(wrapper.find(SomeComponent).exists()); assert.isFalse(wrapper.find(Spinner).exists()); + assert.isFalse(wrapper.find(SomeCustomErrorComponent).exists()); assert.isTrue(wrapper.find(InlineAlert).exists()); }); + + it("render custom error when provided", () => { + const wrapper = mount( + } + renderError={() => } + />, + ); + assert.isFalse(wrapper.find(SomeComponent).exists()); + assert.isFalse(wrapper.find(Spinner).exists()); + assert.isTrue(wrapper.find(SomeCustomErrorComponent).exists()); + }); }); }); diff --git a/pkg/ui/workspaces/cluster-ui/src/loading/loading.tsx b/pkg/ui/workspaces/cluster-ui/src/loading/loading.tsx index ec0fcf42f262..a7bea32f5585 100644 --- a/pkg/ui/workspaces/cluster-ui/src/loading/loading.tsx +++ b/pkg/ui/workspaces/cluster-ui/src/loading/loading.tsx @@ -29,6 +29,7 @@ interface LoadingProps { render: () => any; errorClassName?: string; loadingClassName?: string; + renderError?: () => React.ReactElement; } const cx = classNames.bind(styles); @@ -81,7 +82,11 @@ export const Loading: React.FC = props => { } else { return { intent: "danger", - description: {error.message}, + description: props.renderError ? ( + props.renderError() + ) : ( + {error.message} + ), }; } }) diff --git a/pkg/ui/workspaces/cluster-ui/src/sessions/sessionDetails.tsx b/pkg/ui/workspaces/cluster-ui/src/sessions/sessionDetails.tsx index 40450d93799d..bfe61db5ac4b 100644 --- a/pkg/ui/workspaces/cluster-ui/src/sessions/sessionDetails.tsx +++ b/pkg/ui/workspaces/cluster-ui/src/sessions/sessionDetails.tsx @@ -19,6 +19,7 @@ import { Link, RouteComponentProps } from "react-router-dom"; import { SessionInfo } from "./sessionsTable"; import { SummaryCard, SummaryCardItem } from "../summaryCard"; +import SQLActivityError from "../sqlActivity/errorComponent"; import { TimestampToMoment } from "src/util/convert"; import { Bytes, DATE_FORMAT } from "src/util/format"; @@ -207,6 +208,11 @@ export class SessionDetails extends React.Component { loading={_.isNil(this.props.session)} error={this.props.sessionError} render={this.renderContent} + renderError={() => + SQLActivityError({ + statsType: "sessions", + }) + } /> + SQLActivityError({ + statsType: "sessions", + }) + } /> = props => { + return ( +
+ + This page had an unexpected error while loading + {" " + props.statsType}. + +   + { + window.location.reload(); + }} + > + Reload this page + +
+ ); +}; + +export default SQLActivityError; diff --git a/pkg/ui/workspaces/cluster-ui/src/sqlActivity/sqlActivity.module.scss b/pkg/ui/workspaces/cluster-ui/src/sqlActivity/sqlActivity.module.scss index 30b9152d3332..3854eff0b538 100644 --- a/pkg/ui/workspaces/cluster-ui/src/sqlActivity/sqlActivity.module.scss +++ b/pkg/ui/workspaces/cluster-ui/src/sqlActivity/sqlActivity.module.scss @@ -13,3 +13,8 @@ text-decoration: underline; } } + +.row { + display: flex; + flex-direction: row; +} diff --git a/pkg/ui/workspaces/cluster-ui/src/statementDetails/statementDetails.tsx b/pkg/ui/workspaces/cluster-ui/src/statementDetails/statementDetails.tsx index 18ac90562766..56245594c557 100644 --- a/pkg/ui/workspaces/cluster-ui/src/statementDetails/statementDetails.tsx +++ b/pkg/ui/workspaces/cluster-ui/src/statementDetails/statementDetails.tsx @@ -66,6 +66,7 @@ import { NodeSummaryStats } from "../nodes"; import { UIConfigState } from "../store"; import moment, { Moment } from "moment"; import { StatementsRequest } from "src/api/statementsApi"; +import SQLActivityError from "../sqlActivity/errorComponent"; const { TabPane } = Tabs; @@ -418,6 +419,11 @@ export class StatementDetails extends React.Component< loading={_.isNil(this.props.statement)} error={this.props.statementsError} render={this.renderContent} + renderError={() => + SQLActivityError({ + statsType: "statements", + }) + } /> diff --git a/pkg/ui/workspaces/cluster-ui/src/statementsPage/statementsPage.tsx b/pkg/ui/workspaces/cluster-ui/src/statementsPage/statementsPage.tsx index b9be2351ca80..9b0752c40a0a 100644 --- a/pkg/ui/workspaces/cluster-ui/src/statementsPage/statementsPage.tsx +++ b/pkg/ui/workspaces/cluster-ui/src/statementsPage/statementsPage.tsx @@ -65,6 +65,7 @@ import { UIConfigState } from "../store"; import { StatementsRequest } from "src/api/statementsApi"; import Long from "long"; import ClearStats from "../sqlActivity/clearStats"; +import SQLActivityError from "../sqlActivity/errorComponent"; import { commonStyles } from "../common"; const cx = classNames.bind(styles); @@ -578,6 +579,11 @@ export class StatementsPage extends React.Component< loading={isNil(this.props.statements)} error={this.props.statementsError} render={this.renderStatements} + renderError={() => + SQLActivityError({ + statsType: "statements", + }) + } /> ); }} + renderError={() => + SQLActivityError({ + statsType: "transactions", + }) + } /> ); diff --git a/pkg/ui/workspaces/cluster-ui/src/transactionsPage/transactionsPage.tsx b/pkg/ui/workspaces/cluster-ui/src/transactionsPage/transactionsPage.tsx index 7c6c7e74c83c..154476b65f54 100644 --- a/pkg/ui/workspaces/cluster-ui/src/transactionsPage/transactionsPage.tsx +++ b/pkg/ui/workspaces/cluster-ui/src/transactionsPage/transactionsPage.tsx @@ -59,6 +59,7 @@ import { StatisticTableColumnKeys, } from "../statsTableUtil/statsTableUtil"; import ClearStats from "../sqlActivity/clearStats"; +import SQLActivityError from "../sqlActivity/errorComponent"; import { commonStyles } from "../common"; type IStatementsResponse = protos.cockroach.server.serverpb.IStatementsResponse; @@ -449,6 +450,11 @@ export class TransactionsPage extends React.Component< ); }} + renderError={() => + SQLActivityError({ + statsType: "transactions", + }) + } /> );