Skip to content
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
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import {MergedIssuesSidebarSection} from 'sentry/views/issueDetails/streamline/s
import {PeopleSection} from 'sentry/views/issueDetails/streamline/sidebar/peopleSection';
import {SeerSection} from 'sentry/views/issueDetails/streamline/sidebar/seerSection';
import {SimilarIssuesSidebarSection} from 'sentry/views/issueDetails/streamline/sidebar/similarIssuesSidebarSection';
import {SupergroupSection} from 'sentry/views/issueDetails/streamline/sidebar/supergroupSection';

type Props = {group: Group; project: Project; event?: Event};

Expand Down Expand Up @@ -130,6 +131,9 @@ export function StreamlinedSidebar({group, event, project}: Props) {
{issueTypeConfig.detector.enabled && (
<DetectorSection group={group} project={project} />
)}
<ErrorBoundary mini>
<SupergroupSection group={group} />
</ErrorBoundary>
</Side>
)}
</SharedTourElement>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import {GroupFixture} from 'sentry-fixture/group';
import {OrganizationFixture} from 'sentry-fixture/organization';

import {render, screen} from 'sentry-test/reactTestingLibrary';

import {SupergroupSection} from 'sentry/views/issueDetails/streamline/sidebar/supergroupSection';

describe('SupergroupSection', () => {
it('renders supergroup info when issue belongs to one', async () => {
const organization = OrganizationFixture({features: ['top-issues-ui']});
const group = GroupFixture({id: '1'});
MockApiClient.addMockResponse({
url: `/organizations/${organization.slug}/seer/supergroups/by-group/`,
body: {
data: [
{
id: 10,
title: 'Null pointer in auth flow',
error_type: 'TypeError',
code_area: 'auth/login',
summary: '',
group_ids: [1, 2, 3],
created_at: '2024-01-01T00:00:00Z',
updated_at: '2024-01-01T00:00:00Z',
},
],
},
});

render(<SupergroupSection group={group} />, {organization});

expect(await screen.findByText('TypeError')).toBeInTheDocument();
expect(screen.getByText('Null pointer in auth flow')).toBeInTheDocument();
expect(screen.getByText('3 issues')).toBeInTheDocument();
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
import {Fragment} from 'react';
import styled from '@emotion/styled';

import InteractionStateLayer from '@sentry/scraps/interactionStateLayer';
import {Flex, Stack} from '@sentry/scraps/layout';
import {Text} from '@sentry/scraps/text';

import {useDrawer} from 'sentry/components/globalDrawer';
import {IconStack} from 'sentry/icons';
import {t, tn} from 'sentry/locale';
import type {Group} from 'sentry/types/group';
import {useOrganization} from 'sentry/utils/useOrganization';
import {SidebarSectionTitle} from 'sentry/views/issueDetails/streamline/sidebar/sidebar';
import {SupergroupDetailDrawer} from 'sentry/views/issueList/supergroups/supergroupDrawer';
import {useSuperGroups} from 'sentry/views/issueList/supergroups/useSuperGroups';

interface SupergroupSectionProps {
group: Group;
}

export function SupergroupSection({group}: SupergroupSectionProps) {
const organization = useOrganization();
const {openDrawer} = useDrawer();
const {data: lookup, isLoading} = useSuperGroups([group.id]);
const supergroup = lookup[group.id];

if (!organization.features.includes('top-issues-ui')) {
return null;
}

if (isLoading || !supergroup) {
return null;
}

const issueCount = supergroup.group_ids.length;

const handleClick = () => {
openDrawer(
() => (
<SupergroupDetailDrawer supergroup={supergroup} matchedGroupIds={[group.id]} />
),
{
ariaLabel: t('Supergroup details'),
drawerKey: 'supergroup-drawer',
}
);
};

return (
<div>
<SidebarSectionTitle>{t('Supergroup')}</SidebarSectionTitle>
<SupergroupCard onClick={handleClick} aria-label={t('Supergroup details')}>
<InteractionStateLayer />
<Flex gap="sm" align="start">
<AccentIcon size="sm" />
<Stack gap="xs" style={{overflow: 'hidden'}}>
{supergroup.error_type ? (
<Text size="sm" bold ellipsis>
{supergroup.error_type}
</Text>
) : null}
<Text size="sm" variant="muted" ellipsis>
{supergroup.title}
</Text>
<Flex gap="sm" align="center">
{supergroup.code_area ? (
<Fragment>
<Text size="xs" variant="muted" ellipsis>
{supergroup.code_area}
</Text>
<Dot />
</Fragment>
) : null}
<Text size="xs" variant="muted" style={{flexShrink: 0}}>
{tn('%s issue', '%s issues', issueCount)}
</Text>
</Flex>
</Stack>
</Flex>
</SupergroupCard>
</div>
);
}

const SupergroupCard = styled('button')`
position: relative;
width: 100%;
padding: ${p => p.theme.space.md};
border: 1px solid ${p => p.theme.tokens.border.primary};
border-radius: ${p => p.theme.radius.md};
cursor: pointer;
background: transparent;
text-align: left;
font: inherit;
color: inherit;
`;

const AccentIcon = styled(IconStack)`
color: ${p => p.theme.tokens.graphics.accent.vibrant};
flex-shrink: 0;
margin-top: 2px;
`;

const Dot = styled('div')`
width: 3px;
height: 3px;
border-radius: 50%;
background: currentcolor;
flex-shrink: 0;
`;
Loading