Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ref(ts): Refactor Incident Details to typescript #14510

Merged
merged 8 commits into from
Aug 27, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/sentry/static/sentry/app/routes.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -1041,7 +1041,7 @@ function routes() {
<Route
path=":incidentId/"
componentPromise={() =>
import(/* webpackChunkName: "OrganizationIncidentDetails" */ 'app/views/incidents/details')
import(/* webpackChunkName: "IncidentDetails" */ 'app/views/incidents/details')
}
component={errorHandler(LazyLoad)}
/>
Expand Down
11 changes: 11 additions & 0 deletions src/sentry/static/sentry/app/types/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -291,3 +291,14 @@ export type EventView = {
tags: string[];
columnWidths: string[];
};

export type Repository = {
dateCreated: string;
externalSlug: string;
id: string;
integrationId: string;
name: string;
provider: {id: string; name: string};
status: string;
url: string;
};
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import {Params} from 'react-router/lib/Router';
import React from 'react';
import styled from 'react-emotion';

Expand All @@ -10,7 +11,6 @@ import NavTabs from 'app/components/navTabs';
import Placeholder from 'app/components/placeholder';
import Projects from 'app/utils/projects';
import SeenByList from 'app/components/seenByList';
import SentryTypes from 'app/sentryTypes';
import SideHeader from 'app/views/incidents/details/sideHeader';
import space from 'app/styles/space';
import theme from 'app/utils/theme';
Expand All @@ -19,16 +19,17 @@ import Activity from './activity';
import RelatedIssues from './relatedIssues';
import Suspects from './suspects';

export default class DetailsBody extends React.Component {
static propTypes = {
incident: SentryTypes.Incident,
};
import {Incident} from '../types';

type Props = {
params: Params;
incident?: Incident;
};

export default class DetailsBody extends React.Component<Props> {
render() {
const {params, incident} = this.props;

// Considered loading when there is no incident object
const loading = !incident;
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No longer used because of microsoft/TypeScript#12184

return (
<StyledPageContent>
<Main>
Expand All @@ -51,15 +52,15 @@ export default class DetailsBody extends React.Component {
<Activity
params={params}
incident={incident}
incidentStatus={!loading ? incident.status : null}
incidentStatus={!!incident ? incident.status : null}
/>
</PageContent>
</Main>
<Sidebar>
<PageContent>
<SideHeader loading={loading}>{t('Events in Incident')}</SideHeader>
<SideHeader loading={!incident}>{t('Events in Incident')}</SideHeader>

{!loading ? (
{incident ? (
<Chart
data={incident.eventStats.data}
detected={incident.dateDetected}
Expand All @@ -70,11 +71,11 @@ export default class DetailsBody extends React.Component {
)}

<div>
<SideHeader loading={loading}>
{t('Projects Affected')} ({!loading ? incident.projects.length : '-'})
<SideHeader loading={!incident}>
{t('Projects Affected')} ({incident ? incident.projects.length : '-'})
</SideHeader>

{!loading && (
{incident && (
<div>
<Projects slugs={incident.projects} orgId={params.orgId}>
{({projects}) => {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import PropTypes from 'prop-types';
import React from 'react';
import moment from 'moment';

Expand All @@ -9,6 +8,8 @@ import MarkPoint from 'app/components/charts/components/markPoint';
import closedSymbol from './closedSymbol';
import detectedSymbol from './detectedSymbol';

type Data = [number, {count: number}[]][];

/**
* So we'll have to see how this looks with real data, but echarts requires
* an explicit (x,y) value to draw a symbol (incident detected/closed bubble).
Expand All @@ -18,8 +19,11 @@ import detectedSymbol from './detectedSymbol';
* AFAICT we can't give it an x-axis value and have it draw on the line,
* so we probably need to calculate the y-axis value ourselves if we want it placed
* at the exact time.
*
* @param data Data array
* @param needle the target timestamp
*/
function getNearbyIndex(data, needle) {
function getNearbyIndex(data: Data, needle: number) {
// `data` is sorted, return the first index whose value (timestamp) is > `needle`
const index = data.findIndex(([ts]) => ts > needle);

Expand All @@ -31,20 +35,13 @@ function getNearbyIndex(data, needle) {
return index !== -1 ? index - 1 : data.length - 1;
}

export default class Chart extends React.PureComponent {
static propTypes = {
data: PropTypes.arrayOf(
PropTypes.arrayOf(
PropTypes.oneOfType([
PropTypes.number,
PropTypes.arrayOf(PropTypes.shape({count: PropTypes.number})),
])
)
),
detected: PropTypes.string,
closed: PropTypes.string,
};
type Props = {
data: Data;
detected: string;
closed: string;
};

export default class Chart extends React.PureComponent<Props> {
render() {
const {data, detected, closed} = this.props;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import {Params} from 'react-router/lib/Router';
import {Link} from 'react-router';
import PropTypes from 'prop-types';
import React from 'react';
import moment from 'moment';
import styled from 'react-emotion';
Expand All @@ -14,23 +14,25 @@ import InlineSvg from 'app/components/inlineSvg';
import LoadingError from 'app/components/loadingError';
import MenuItem from 'app/components/menuItem';
import PageHeading from 'app/components/pageHeading';
import SentryTypes from 'app/sentryTypes';
import SubscribeButton from 'app/components/subscribeButton';
import space from 'app/styles/space';
import getDynamicText from 'app/utils/getDynamicText';

import {isOpen} from '../utils';
import Status from '../status';

export default class DetailsHeader extends React.Component {
static propTypes = {
incident: SentryTypes.Incident,
params: PropTypes.object.isRequired,
hasIncidentDetailsError: PropTypes.bool.isRequired,
onSubscriptionChange: PropTypes.func.isRequired,
onStatusChange: PropTypes.func.isRequired,
};

import {Incident} from '../types';

type Props = {
className?: string;
hasIncidentDetailsError: boolean;
// Can be undefined when loading
incident?: Incident;
onSubscriptionChange: (event: React.MouseEvent<HTMLButtonElement, MouseEvent>) => void;
onStatusChange: (eventKey: any) => void;
params: Params;
};

export default class DetailsHeader extends React.Component<Props> {
renderStatus() {
const {incident, onStatusChange} = this.props;

Expand Down Expand Up @@ -59,15 +61,18 @@ export default class DetailsHeader extends React.Component {
render() {
const {hasIncidentDetailsError, incident, params, onSubscriptionChange} = this.props;
const isIncidentReady = !!incident && !hasIncidentDetailsError;
const eventLink = incident && {
pathname: `/organizations/${params.orgId}/events/`,

// Note we don't have project selector on here so there should be
// no query params to forward
query: {
group: incident.groups,
},
};
const eventLink = incident
? {
pathname: `/organizations/${params.orgId}/events/`,

// Note we don't have project selector on here so there should be
// no query params to forward
query: {
group: incident.groups,
},
}
: '';

const dateStarted = incident && moment(incident.dateStarted).format('LL');
const duration =
incident &&
Expand All @@ -93,7 +98,7 @@ export default class DetailsHeader extends React.Component {
)}
</Breadcrumb>
<IncidentTitle data-test-id="incident-title" loading={!isIncidentReady}>
{isIncidentReady ? incident.title : 'Loading'}
{incident && !hasIncidentDetailsError ? incident.title : 'Loading'}
</IncidentTitle>
</PageHeading>
</HeaderItem>
Expand All @@ -107,23 +112,23 @@ export default class DetailsHeader extends React.Component {
</HeaderItem>
<HeaderItem>
<ItemTitle>{t('Duration')}</ItemTitle>
{isIncidentReady && (
{incident && (
<ItemValue>
<Duration seconds={getDynamicText({value: duration, fixed: 1200})} />
</ItemValue>
)}
</HeaderItem>
<HeaderItem>
<ItemTitle>{t('Users affected')}</ItemTitle>
{isIncidentReady && (
{incident && (
<ItemValue>
<Count value={incident.uniqueUsers} />
</ItemValue>
)}
</HeaderItem>
<HeaderItem>
<ItemTitle>{t('Total events')}</ItemTitle>
{isIncidentReady && (
{incident && (
<ItemValue>
<Count value={incident.totalEvents} />
<OpenLink to={eventLink}>
Expand Down Expand Up @@ -198,7 +203,7 @@ const Breadcrumb = styled('div')`
margin-bottom: ${space(1)};
`;

const IncidentTitle = styled('div')`
const IncidentTitle = styled('div')<{loading: boolean}>`
${p => p.loading && 'opacity: 0'};
`;

Expand Down
Loading