Skip to content

Commit

Permalink
feat(issue-details): Add streamlined sidebar and activity (#77958)
Browse files Browse the repository at this point in the history
this pr adds in the dismissible sidebar and a first version of adding
activity to the sidebar. the sidebar can now be dismissed, allowing for
the rest of the issue details page to be displayed larger. the first
version of activity in the sidebar will only show activities, the
ability to comment is not added yet (will be done in a separate PR).
Because of this, the tab will still be there until then.


![Screenshot 2024-09-23 at 10 03
30 AM](https://github.com/user-attachments/assets/1c092938-09ed-474c-a85f-e00fcf2f2108)
  • Loading branch information
roggenkemper authored Sep 24, 2024
1 parent 583a084 commit 2446a87
Show file tree
Hide file tree
Showing 7 changed files with 936 additions and 35 deletions.
4 changes: 2 additions & 2 deletions static/app/components/layouts/thirds.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ export const HeaderNavTabs = styled(NavTabs)`
/**
* Base container for 66/33 containers.
*/
export const Body = styled('div')<{noRowGap?: boolean}>`
export const Body = styled('div')<{noRowGap?: boolean; sidebarOpen?: boolean}>`
padding: ${space(2)};
margin: 0;
background-color: ${p => p.theme.background};
Expand All @@ -132,7 +132,7 @@ export const Body = styled('div')<{noRowGap?: boolean}>`
}
@media (min-width: ${p => p.theme.breakpoints.large}) {
display: grid;
display: ${p => (p.sidebarOpen ? 'grid' : 'block')};
grid-template-columns: minmax(100px, auto) 325px;
align-content: start;
gap: ${p => (!p.noRowGap ? `${space(3)}` : `0 ${space(3)}`)};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,11 @@ import {getConfigForIssueType} from 'sentry/utils/issueTypeConfig';
import {VisuallyCompleteWithData} from 'sentry/utils/performanceForSentry';
import normalizeUrl from 'sentry/utils/url/normalizeUrl';
import usePrevious from 'sentry/utils/usePrevious';
import {useSyncedLocalStorageState} from 'sentry/utils/useSyncedLocalStorageState';
import GroupEventDetailsContent from 'sentry/views/issueDetails/groupEventDetails/groupEventDetailsContent';
import GroupEventHeader from 'sentry/views/issueDetails/groupEventHeader';
import GroupSidebar from 'sentry/views/issueDetails/groupSidebar';
import StreamlinedSidebar from 'sentry/views/issueDetails/streamline/sidebar';

import ReprocessingProgress from '../reprocessingProgress';
import {
Expand Down Expand Up @@ -72,6 +74,11 @@ function GroupEventDetails(props: GroupEventDetailsProps) {
const prevEvent = usePrevious(event);
const hasStreamlinedUI = useHasStreamlinedUI();

const [sidebarOpen, _] = useSyncedLocalStorageState(
'issue-details-sidebar-open',
false
);

// load the data
useSentryAppComponentsData({projectId});

Expand Down Expand Up @@ -172,6 +179,7 @@ function GroupEventDetails(props: GroupEventDetailsProps) {
<StyledLayoutBody
data-test-id="group-event-details"
hasStreamlinedUi={hasStreamlinedUI}
sidebarOpen={sidebarOpen}
>
{groupReprocessingStatus === ReprocessingStatus.REPROCESSING ? (
<ReprocessingProgress
Expand All @@ -197,15 +205,23 @@ function GroupEventDetails(props: GroupEventDetailsProps) {
)}
{renderContent()}
</MainLayoutComponent>
<StyledLayoutSide hasStreamlinedUi={hasStreamlinedUI}>
<GroupSidebar
organization={organization}
project={project}
group={group}
event={eventWithMeta}
environments={environments}
/>
</StyledLayoutSide>
{hasStreamlinedUI ? (
sidebarOpen ? (
<StyledLayoutSide hasStreamlinedUi={hasStreamlinedUI}>
<StreamlinedSidebar group={group} event={event} project={project} />
</StyledLayoutSide>
) : null
) : (
<StyledLayoutSide hasStreamlinedUi={hasStreamlinedUI}>
<GroupSidebar
organization={organization}
project={project}
group={group}
event={eventWithMeta}
environments={environments}
/>
</StyledLayoutSide>
)}
</Fragment>
)}
</StyledLayoutBody>
Expand All @@ -214,7 +230,10 @@ function GroupEventDetails(props: GroupEventDetailsProps) {
);
}

const StyledLayoutBody = styled(Layout.Body)<{hasStreamlinedUi: boolean}>`
const StyledLayoutBody = styled(Layout.Body)<{
hasStreamlinedUi: boolean;
sidebarOpen: boolean;
}>`
/* Makes the borders align correctly */
padding: 0 !important;
@media (min-width: ${p => p.theme.breakpoints.large}) {
Expand Down
50 changes: 50 additions & 0 deletions static/app/views/issueDetails/streamline/activitySection.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import {Fragment} from 'react';
import styled from '@emotion/styled';

import Timeline from 'sentry/components/timeline';
import TimeSince from 'sentry/components/timeSince';
import type {Group} from 'sentry/types/group';
import useOrganization from 'sentry/utils/useOrganization';
import {groupActivityTypeIconMapping} from 'sentry/views/issueDetails/streamline/groupActivityIcons';
import getGroupActivityItem from 'sentry/views/issueDetails/streamline/groupActivityItem';

function StreamlinedActivitySection({group}: {group: Group}) {
const organization = useOrganization();

return (
<Fragment>
<Timeline.Container>
{group.activity.map(item => {
const authorName = item.user ? item.user.name : 'Sentry';
const {title, message} = getGroupActivityItem(
item,
organization,
group.project.id,
<Author>{authorName}</Author>
);

const Icon = groupActivityTypeIconMapping[item.type]?.Component ?? null;

return (
<Timeline.Item
title={title}
timestamp={<TimeSince date={item.dateCreated} />}
icon={
Icon && <Icon {...groupActivityTypeIconMapping[item.type].defaultProps} />
}
key={item.id}
>
{message}
</Timeline.Item>
);
})}
</Timeline.Container>
</Fragment>
);
}

const Author = styled('span')`
font-weight: ${p => p.theme.fontWeightBold};
`;

export default StreamlinedActivitySection;
67 changes: 67 additions & 0 deletions static/app/views/issueDetails/streamline/groupActivityIcons.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import {
IconAdd,
IconCheckmark,
IconClose,
IconDelete,
IconEdit,
IconFile,
IconFire,
IconFlag,
IconGraph,
IconLock,
IconMute,
IconNext,
IconPlay,
IconPrevious,
IconRefresh,
IconUnsubscribed,
IconUser,
} from 'sentry/icons';
import {GroupActivityType} from 'sentry/types/group';

interface IconWithDefaultProps {
Component: React.ComponentType<any> | null;
defaultProps: {locked?: boolean; type?: string};
}

export const groupActivityTypeIconMapping: Record<
GroupActivityType,
IconWithDefaultProps
> = {
[GroupActivityType.NOTE]: {Component: IconFile, defaultProps: {}},
[GroupActivityType.SET_RESOLVED]: {Component: IconCheckmark, defaultProps: {}},
[GroupActivityType.SET_RESOLVED_BY_AGE]: {Component: IconCheckmark, defaultProps: {}},
[GroupActivityType.SET_RESOLVED_IN_RELEASE]: {
Component: IconCheckmark,
defaultProps: {},
},
[GroupActivityType.SET_RESOLVED_IN_COMMIT]: {
Component: IconCheckmark,
defaultProps: {},
},
[GroupActivityType.SET_RESOLVED_IN_PULL_REQUEST]: {
Component: IconCheckmark,
defaultProps: {},
},
[GroupActivityType.SET_UNRESOLVED]: {Component: IconClose, defaultProps: {}},
[GroupActivityType.SET_IGNORED]: {Component: IconMute, defaultProps: {}},
[GroupActivityType.SET_PUBLIC]: {Component: IconLock, defaultProps: {}},
[GroupActivityType.SET_PRIVATE]: {Component: IconLock, defaultProps: {locked: true}},
[GroupActivityType.SET_REGRESSION]: {Component: IconFire, defaultProps: {}},
[GroupActivityType.CREATE_ISSUE]: {Component: IconAdd, defaultProps: {}},
[GroupActivityType.UNMERGE_SOURCE]: {Component: IconPrevious, defaultProps: {}},
[GroupActivityType.UNMERGE_DESTINATION]: {Component: IconPrevious, defaultProps: {}},
[GroupActivityType.FIRST_SEEN]: {Component: IconFlag, defaultProps: {}},
[GroupActivityType.ASSIGNED]: {Component: IconUser, defaultProps: {}},
[GroupActivityType.UNASSIGNED]: {Component: IconUnsubscribed, defaultProps: {}},
[GroupActivityType.MERGE]: {Component: IconNext, defaultProps: {}},
[GroupActivityType.REPROCESS]: {Component: IconRefresh, defaultProps: {}},
[GroupActivityType.MARK_REVIEWED]: {Component: IconCheckmark, defaultProps: {}},
[GroupActivityType.AUTO_SET_ONGOING]: {Component: IconPlay, defaultProps: {}},
[GroupActivityType.SET_ESCALATING]: {
Component: IconGraph,
defaultProps: {type: 'area'},
},
[GroupActivityType.SET_PRIORITY]: {Component: IconEdit, defaultProps: {}},
[GroupActivityType.DELETED_ATTACHMENT]: {Component: IconDelete, defaultProps: {}},
};
Loading

0 comments on commit 2446a87

Please sign in to comment.