Skip to content

feat(events-v2) Add linked issue preview on event modal #13505

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

Merged
merged 3 commits into from
Jun 4, 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
12 changes: 3 additions & 9 deletions src/sentry/static/sentry/app/components/seenByList.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -47,14 +47,8 @@ export default class SeenByList extends React.Component {
iconTooltip,
} = this.props;

// NOTE: Sometimes group.seenBy is undefined, even though the /groups/{id} API
// endpoint guarantees an array. We haven't figured out HOW GroupSeenBy
// is getting incomplete group records, but in the interim, we are just
// gracefully handing this case.
//
// See: https://github.com/getsentry/sentry/issues/2387

if (seenBy.length === 0) {
const displayUsers = seenBy.filter(user => activeUser.id !== user.id);
if (displayUsers.length === 0) {
return null;
}

Expand All @@ -65,7 +59,7 @@ export default class SeenByList extends React.Component {
className={classNames('seen-by', className)}
>
<AvatarList
users={seenBy.filter(user => activeUser.id !== user.id)}
users={displayUsers}
avatarSize={avatarSize}
maxVisibleAvatars={maxVisibleAvatars}
renderTooltip={user => (
Expand Down
22 changes: 14 additions & 8 deletions src/sentry/static/sentry/app/components/stream/groupChart.jsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
import LazyLoad from 'react-lazyload';
import PropTypes from 'prop-types';
import React from 'react';
import styled from 'react-emotion';

import BarChart from 'app/components/barChart';

const StyledBarChart = styled(BarChart)`
height: 24px;
`;

class GroupChart extends React.Component {
static propTypes = {
statsPeriod: PropTypes.string.isRequired,
data: PropTypes.object.isRequired,
height: PropTypes.number,
};

static defaultProps = {
height: 24,
};

shouldComponentUpdate(nextProps) {
Expand All @@ -30,14 +30,20 @@ class GroupChart extends React.Component {
if (!stats || !stats.length) {
return null;
}

const {height} = this.props;
const chartData = stats.map(point => {
return {x: point[0], y: point[1]};
});

return (
<LazyLoad debounce={50} height={24}>
<StyledBarChart points={chartData} label="events" minHeights={[3]} gap={1} />
<LazyLoad debounce={50} height={height}>
<BarChart
points={chartData}
height={height}
label="events"
minHeights={[3]}
gap={1}
/>
</LazyLoad>
);
}
Expand Down
6 changes: 3 additions & 3 deletions src/sentry/static/sentry/app/utils/theme.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -91,14 +91,14 @@ const theme = {
sidebar: 1010,
orgAndUserMenu: 1011,

// tooltips and hovercards
tooltip: 1070,

// Sentry user feedback modal
sentryErrorEmbed: 1090,

modal: 10000,
toast: 10001,

// tooltips and hovercards can be inside modals sometimes.
tooltip: 10002,
Copy link
Member

Choose a reason for hiding this comment

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

How does this affect tooltips app-wide?

Copy link
Member Author

Choose a reason for hiding this comment

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

I think this is one of the first situations where we need to show tooltips inside a modal window. Keeping focus/hover on an element when a modal is open is pretty hard so I don't forsee this causing many problems.

},

grid: 8,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import {getMessage, getTitle} from 'app/utils/events';

import {INTERFACES} from 'app/components/events/eventEntries';
import TagsTable from './tagsTable';
import LinkedIssuePreview from './linkedIssuePreview';

const OTHER_SECTIONS = {
context: EventExtraData,
Expand Down Expand Up @@ -127,6 +128,7 @@ const EventModalContent = props => {
</ErrorBoundary>
</ContentColumn>
<SidebarColumn>
{event.groupID && <LinkedIssuePreview groupId={event.groupID} />}
<EventMetadata event={event} />
<SidebarBlock>
<TagsTable tags={event.tags} />
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
import React from 'react';
import styled from 'react-emotion';
import PropTypes from 'prop-types';

import {t} from 'app/locale';
import AsyncComponent from 'app/components/asyncComponent';
import GroupChart from 'app/components/stream/groupChart';
import InlineSvg from 'app/components/inlineSvg';
import Link from 'app/components/links/link';
import ProjectBadge from 'app/components/idBadge/projectBadge';
import SeenByList from 'app/components/seenByList';
import ShortId from 'app/components/shortId';
import Times from 'app/components/group/times';
import space from 'app/styles/space';

class LinkedIssuePreview extends AsyncComponent {
static propTypes = {
groupId: PropTypes.string.isRequired,
};

getEndpoints() {
const {groupId} = this.props;
const groupUrl = `/issues/${groupId}/`;

return [['group', groupUrl]];
}

renderBody() {
const {group} = this.state;

return (
<Container>
<Title>
<InlineSvg src="icon-link" size="12px" /> {t('Linked Issue')}
</Title>
<Section>
<Link to={group.permalink}>
<StyledShortId
shortId={group.shortId}
avatar={<ProjectBadge project={group.project} avatarSize={16} hideName />}
/>
</Link>
<StyledSeenByList seenBy={group.seenBy} maxVisibleAvatars={5} />
</Section>
<ChartContainer>
<GroupChart id={group.id} statsPeriod="30d" data={group} height={48} />
</ChartContainer>
<TimesSection>
<Times lastSeen={group.lastSeen} firstSeen={group.firstSeen} />
</TimesSection>
</Container>
);
}
}

const Container = styled('div')`
border: 1px solid ${p => p.theme.borderLight};
border-radius: ${p => p.theme.borderRadius};
margin-bottom: ${space(2)};
position: relative;
`;

const Title = styled('h4')`
background: #fff;
color: ${p => p.theme.gray3};
padding: 0 ${space(0.5)};

font-size: ${p => p.theme.fontSizeSmall};
font-weight: normal;

top: -${space(1)};
left: ${space(1)};
position: absolute;
`;

const Section = styled('div')`
display: flex;
justify-content: space-between;
padding: ${space(1)};
`;

const ChartContainer = styled('div')`
border-top: 1px solid ${p => p.theme.borderLight};
border-bottom: 1px solid ${p => p.theme.borderLight};
background: ${p => p.theme.offWhite};
position: relative;
`;

const StyledSeenByList = styled(SeenByList)`
margin: 0;
`;

const StyledShortId = styled(ShortId)`
font-size: ${p => p.theme.fontSizeMedium};
`;

const TimesSection = styled(Section)`
color: ${p => p.theme.gray2};
font-size: ${p => p.theme.fontSizeSmall};
`;

export default LinkedIssuePreview;
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,7 @@ exports[`StreamGroup renders with anchors 1`] = `
"userReportCount": 0,
}
}
height={24}
id="1337"
statsPeriod="24h"
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -486,7 +486,7 @@ exports[`Sentry App Installations when Apps exist displays all Apps owned by the
"sentryErrorEmbed": 1090,
"sidebar": 1010,
"toast": 10001,
"tooltip": 1070,
"tooltip": 10002,
},
}
}
Expand Down Expand Up @@ -759,7 +759,7 @@ exports[`Sentry App Installations when Apps exist displays all Apps owned by the
"sentryErrorEmbed": 1090,
"sidebar": 1010,
"toast": 10001,
"tooltip": 1070,
"tooltip": 10002,
},
}
}
Expand Down