mirror of
https://github.com/prymitive/karma
synced 2026-05-07 03:26:52 +00:00
Merge pull request #1074 from prymitive/more-ts
chore(ui): migrate App component to typescript
This commit is contained in:
@@ -1,5 +1,4 @@
|
||||
import React, { Component } from "react";
|
||||
import PropTypes from "prop-types";
|
||||
|
||||
import { Provider } from "mobx-react";
|
||||
|
||||
@@ -14,24 +13,27 @@ import { ErrorBoundary } from "./ErrorBoundary";
|
||||
|
||||
import "./App.scss";
|
||||
|
||||
class App extends Component {
|
||||
static propTypes = {
|
||||
defaultFilters: PropTypes.arrayOf(PropTypes.string).isRequired,
|
||||
uiDefaults: PropTypes.exact({
|
||||
Refresh: PropTypes.number.isRequired,
|
||||
HideFiltersWhenIdle: PropTypes.bool.isRequired,
|
||||
ColorTitlebar: PropTypes.bool.isRequired,
|
||||
MinimalGroupWidth: PropTypes.number.isRequired,
|
||||
AlertsPerGroup: PropTypes.number.isRequired,
|
||||
CollapseGroups: PropTypes.oneOf([
|
||||
"expanded",
|
||||
"collapsed",
|
||||
"collapsedOnMobile"
|
||||
]).isRequired
|
||||
})
|
||||
};
|
||||
interface UIDefaults {
|
||||
Refresh: number;
|
||||
HideFiltersWhenIdle: boolean;
|
||||
ColorTitlebar: boolean;
|
||||
MinimalGroupWidth: number;
|
||||
AlertsPerGroup: number;
|
||||
CollapseGroups: "expanded" | "collapsed" | "collapsedOnMobile";
|
||||
}
|
||||
|
||||
constructor(props) {
|
||||
interface AppProps {
|
||||
defaultFilters: Array<string>;
|
||||
uiDefaults: UIDefaults;
|
||||
}
|
||||
|
||||
class App extends Component<AppProps, {}> {
|
||||
alertStore: AlertStore;
|
||||
silenceFormStore: SilenceFormStore;
|
||||
settingsStore: Settings;
|
||||
filters: Array<string> = [];
|
||||
|
||||
constructor(props: AppProps) {
|
||||
super(props);
|
||||
|
||||
const { defaultFilters, uiDefaults } = this.props;
|
||||
@@ -52,7 +52,7 @@ describe("<ErrorBoundary />", () => {
|
||||
});
|
||||
|
||||
it("componentDidCatch passes scope to sentry", () => {
|
||||
const sentrySpy = jest.spyOn(Sentry, "configureScope");
|
||||
const sentrySpy = jest.spyOn(Sentry, "captureException");
|
||||
Sentry.init({ dsn: "https://foobar@localhost/123456" });
|
||||
|
||||
const tree = mount(
|
||||
|
||||
@@ -1,12 +1,22 @@
|
||||
import React, { Component } from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import React, {
|
||||
Component,
|
||||
StatelessComponent,
|
||||
ReactNode,
|
||||
ErrorInfo
|
||||
} from "react";
|
||||
|
||||
import * as Sentry from "@sentry/browser";
|
||||
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { faBomb } from "@fortawesome/free-solid-svg-icons/faBomb";
|
||||
|
||||
const InternalError = ({ message, secondsLeft, progressLeft }) => (
|
||||
interface InternalErrorProps {
|
||||
message: ReactNode;
|
||||
secondsLeft: number;
|
||||
progressLeft: number;
|
||||
}
|
||||
|
||||
const InternalError: StatelessComponent<InternalErrorProps> = props => (
|
||||
<div className="jumbotron text-center bg-primary my-4">
|
||||
<div className="container-fluid">
|
||||
<h1 className="display-1 my-5">
|
||||
@@ -14,10 +24,10 @@ const InternalError = ({ message, secondsLeft, progressLeft }) => (
|
||||
<span className="text-muted">Internal error</span>
|
||||
</h1>
|
||||
<p className="lead text-white bg-secondary px-1 py-3 rounded">
|
||||
{message}
|
||||
{props.message}
|
||||
</p>
|
||||
<p className="text-muted d-inline-block">
|
||||
This page will auto refresh in {secondsLeft}s
|
||||
This page will auto refresh in {props.secondsLeft}s
|
||||
<span
|
||||
className="progress bg-secondary mx-auto"
|
||||
style={{ height: "2px" }}
|
||||
@@ -25,31 +35,36 @@ const InternalError = ({ message, secondsLeft, progressLeft }) => (
|
||||
<span
|
||||
className="progress-bar bg-info"
|
||||
role="progressbar"
|
||||
style={{ width: `${progressLeft}%` }}
|
||||
aria-valuenow={progressLeft}
|
||||
aria-valuemin="0"
|
||||
aria-valuemax="100"
|
||||
style={{ width: `${props.progressLeft}%` }}
|
||||
aria-valuenow={props.progressLeft}
|
||||
aria-valuemin={0}
|
||||
aria-valuemax={100}
|
||||
></span>
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
InternalError.propTypes = {
|
||||
message: PropTypes.node.isRequired,
|
||||
secondsLeft: PropTypes.number.isRequired,
|
||||
progressLeft: PropTypes.number.isRequired
|
||||
};
|
||||
|
||||
class ErrorBoundary extends Component {
|
||||
static propTypes = {
|
||||
children: PropTypes.any
|
||||
interface ErrorBoundaryProps {
|
||||
children: React.ReactNode;
|
||||
}
|
||||
|
||||
interface ErrorBoundaryState {
|
||||
cachedError: Error | null;
|
||||
reloadSeconds: number;
|
||||
}
|
||||
|
||||
class ErrorBoundary extends Component<ErrorBoundaryProps, ErrorBoundaryState> {
|
||||
timer: ReturnType<typeof setInterval> | null;
|
||||
state: Readonly<ErrorBoundaryState> = {
|
||||
cachedError: null,
|
||||
reloadSeconds: 60
|
||||
};
|
||||
|
||||
constructor(props) {
|
||||
constructor(props: ErrorBoundaryProps) {
|
||||
super(props);
|
||||
this.timer = null;
|
||||
this.state = { cachedError: null, reloadSeconds: 60 };
|
||||
}
|
||||
|
||||
reloadApp = () => {
|
||||
@@ -60,14 +75,12 @@ class ErrorBoundary extends Component {
|
||||
}
|
||||
};
|
||||
|
||||
componentDidCatch(error, errorInfo) {
|
||||
componentDidCatch(error: Error | null, errorInfo: ErrorInfo) {
|
||||
this.setState({ cachedError: error });
|
||||
Sentry.configureScope(scope => {
|
||||
Object.keys(errorInfo).forEach(key => {
|
||||
scope.setExtra(key, errorInfo[key]);
|
||||
});
|
||||
Sentry.withScope(scope => {
|
||||
scope.setExtras(errorInfo);
|
||||
Sentry.captureException(error);
|
||||
});
|
||||
Sentry.captureException(error);
|
||||
// reload after 60s, this is to fix wall monitors automatically
|
||||
// but only if the timer isn't set yet
|
||||
if (this.timer === null) {
|
||||
Reference in New Issue
Block a user