From 5355d93679cee1d2160e220c5f42a3d58a663e21 Mon Sep 17 00:00:00 2001 From: Richard Roggenkemper <46740234+roggenkemper@users.noreply.github.com> Date: Wed, 23 Oct 2024 11:49:07 -0700 Subject: [PATCH] feat(issue-details): Add people section to sidebar (#79609) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit this pr removes participants/viewers from the header and moves them into their own section in the sidebar ![Screenshot 2024-10-23 at 9 50 05 AM](https://github.com/user-attachments/assets/6737bd6c-4450-4cc4-920a-7c89629f62f8) --- .../issueDetails/streamline/header.spec.tsx | 33 +----------- .../views/issueDetails/streamline/header.tsx | 31 +---------- .../streamline/peopleSection.spec.tsx | 52 +++++++++++++++++++ .../issueDetails/streamline/peopleSection.tsx | 49 +++++++++++++++++ .../views/issueDetails/streamline/sidebar.tsx | 9 ++++ 5 files changed, 113 insertions(+), 61 deletions(-) create mode 100644 static/app/views/issueDetails/streamline/peopleSection.spec.tsx create mode 100644 static/app/views/issueDetails/streamline/peopleSection.tsx diff --git a/static/app/views/issueDetails/streamline/header.spec.tsx b/static/app/views/issueDetails/streamline/header.spec.tsx index 922d97fb9b2ae2..37d51dffd40933 100644 --- a/static/app/views/issueDetails/streamline/header.spec.tsx +++ b/static/app/views/issueDetails/streamline/header.spec.tsx @@ -4,11 +4,9 @@ import {OrganizationFixture} from 'sentry-fixture/organization'; import {ProjectFixture} from 'sentry-fixture/project'; import {RouterFixture} from 'sentry-fixture/routerFixture'; import {TeamFixture} from 'sentry-fixture/team'; -import {UserFixture} from 'sentry-fixture/user'; import {render, screen} from 'sentry-test/reactTestingLibrary'; -import type {TeamParticipant, UserParticipant} from 'sentry/types/group'; import {IssueCategory} from 'sentry/types/group'; import {formatAbbreviatedNumber} from 'sentry/utils/formatters'; import StreamlinedGroupHeader from 'sentry/views/issueDetails/streamline/header'; @@ -56,36 +54,10 @@ describe('StreamlinedGroupHeader', () => { }); it('shows all elements of header', async () => { - const teams: TeamParticipant[] = [{...TeamFixture(), type: 'team'}]; - const users: UserParticipant[] = [ - { - ...UserFixture({ - id: '2', - name: 'John Smith', - email: 'johnsmith@example.com', - }), - type: 'user', - }, - { - ...UserFixture({ - id: '3', - name: 'Sohn Jmith', - email: 'sohnjmith@example.com', - }), - type: 'user', - }, - ]; - - const participantGroup = { - ...group, - participants: [...teams, ...users], - seenBy: users, - }; - render( , @@ -110,9 +82,6 @@ describe('StreamlinedGroupHeader', () => { screen.getByRole('button', {name: 'Modify issue assignee'}) ).toBeInTheDocument(); - expect(screen.getByText('Participants')).toBeInTheDocument(); - expect(screen.getByText('Viewers')).toBeInTheDocument(); - expect( screen.queryByRole('button', {name: 'Switch to the old issue experience'}) ).not.toBeInTheDocument(); diff --git a/static/app/views/issueDetails/streamline/header.tsx b/static/app/views/issueDetails/streamline/header.tsx index 29d5f316767b3b..08847a4217382c 100644 --- a/static/app/views/issueDetails/streamline/header.tsx +++ b/static/app/views/issueDetails/streamline/header.tsx @@ -1,4 +1,4 @@ -import {Fragment, useMemo} from 'react'; +import {Fragment} from 'react'; import styled from '@emotion/styled'; import Color from 'color'; @@ -8,20 +8,18 @@ import {Flex} from 'sentry/components/container/flex'; import Count from 'sentry/components/count'; import ErrorLevel from 'sentry/components/events/errorLevel'; import UnhandledTag from 'sentry/components/group/inboxBadges/unhandledTag'; -import ParticipantList from 'sentry/components/group/streamlinedParticipantList'; import Link from 'sentry/components/links/link'; import {Tooltip} from 'sentry/components/tooltip'; import {IconChevron, IconPanel} from 'sentry/icons'; import {t} from 'sentry/locale'; import {space} from 'sentry/styles/space'; import type {Event} from 'sentry/types/event'; -import type {Group, TeamParticipant, UserParticipant} from 'sentry/types/group'; +import type {Group} from 'sentry/types/group'; import type {Project} from 'sentry/types/project'; import {getMessage, getTitle} from 'sentry/utils/events'; import {useLocation} from 'sentry/utils/useLocation'; import useOrganization from 'sentry/utils/useOrganization'; import {useSyncedLocalStorageState} from 'sentry/utils/useSyncedLocalStorageState'; -import {useUser} from 'sentry/utils/useUser'; import GroupActions from 'sentry/views/issueDetails/actions/index'; import {NewIssueExperienceButton} from 'sentry/views/issueDetails/actions/newIssueExperienceButton'; import {Divider} from 'sentry/views/issueDetails/divider'; @@ -47,7 +45,6 @@ export default function StreamlinedGroupHeader({ groupReprocessingStatus, project, }: GroupHeaderProps) { - const activeUser = useUser(); const location = useLocation(); const organization = useOrganization(); const {baseUrl} = useGroupDetailsRoute(); @@ -65,18 +62,6 @@ export default function StreamlinedGroupHeader({ true ); - const {userParticipants, teamParticipants, displayUsers} = useMemo(() => { - return { - userParticipants: group.participants.filter( - (p): p is UserParticipant => p.type === 'user' - ), - teamParticipants: group.participants.filter( - (p): p is TeamParticipant => p.type === 'team' - ), - displayUsers: group.seenBy.filter(user => activeUser.id !== user.id), - }; - }, [group, activeUser.id]); - return (
@@ -163,18 +148,6 @@ export default function StreamlinedGroupHeader({ {t('Assignee')} - {group.participants.length > 0 && ( - - {t('Participants')} - - - )} - {displayUsers.length > 0 && ( - - {t('Viewers')} - - - )} { + const group = GroupFixture(); + + it('displays participants and viewers', async () => { + const teams: TeamParticipant[] = [{...TeamFixture(), type: 'team'}]; + const users: UserParticipant[] = [ + { + ...UserFixture({ + id: '2', + name: 'John Smith', + email: 'johnsmith@example.com', + }), + type: 'user', + }, + { + ...UserFixture({ + id: '3', + name: 'Sohn Jmith', + email: 'sohnjmith@example.com', + }), + type: 'user', + }, + ]; + + const participantGroup = { + ...group, + participants: [...teams, ...users], + seenBy: users, + }; + + render(); + + expect(await screen.findByText('participating')).toBeInTheDocument(); + expect(await screen.findByText('viewed')).toBeInTheDocument(); + }); + + it('does not display anything if there are no participants or viewers', () => { + render(); + + expect(screen.queryByText('participating')).not.toBeInTheDocument(); + expect(screen.queryByText('viewed')).not.toBeInTheDocument(); + }); +}); diff --git a/static/app/views/issueDetails/streamline/peopleSection.tsx b/static/app/views/issueDetails/streamline/peopleSection.tsx new file mode 100644 index 00000000000000..8e4a5e403da9f0 --- /dev/null +++ b/static/app/views/issueDetails/streamline/peopleSection.tsx @@ -0,0 +1,49 @@ +import {useMemo} from 'react'; + +import {Flex} from 'sentry/components/container/flex'; +import ParticipantList from 'sentry/components/group/streamlinedParticipantList'; +import {t} from 'sentry/locale'; +import {space} from 'sentry/styles/space'; +import type {Group, TeamParticipant, UserParticipant} from 'sentry/types/group'; +import {useUser} from 'sentry/utils/useUser'; +import {SidebarSectionTitle} from 'sentry/views/issueDetails/streamline/sidebar'; + +export default function PeopleSection({group}: {group: Group}) { + const activeUser = useUser(); + const {userParticipants, teamParticipants, viewers} = useMemo(() => { + return { + userParticipants: group.participants.filter( + (p): p is UserParticipant => p.type === 'user' + ), + teamParticipants: group.participants.filter( + (p): p is TeamParticipant => p.type === 'team' + ), + viewers: group.seenBy.filter(user => activeUser.id !== user.id), + }; + }, [group, activeUser.id]); + + const hasParticipants = group.participants.length > 0; + const hasViewers = viewers.length > 0; + + if (!hasParticipants && !hasViewers) { + return null; + } + + return ( +
+ {t('People')} + {hasParticipants && ( + + + {t('participating')} + + )} + {hasViewers && ( + + + {t('viewed')} + + )} +
+ ); +} diff --git a/static/app/views/issueDetails/streamline/sidebar.tsx b/static/app/views/issueDetails/streamline/sidebar.tsx index cbce1573b23486..f6643ea774ea3c 100644 --- a/static/app/views/issueDetails/streamline/sidebar.tsx +++ b/static/app/views/issueDetails/streamline/sidebar.tsx @@ -2,12 +2,14 @@ import styled from '@emotion/styled'; import ErrorBoundary from 'sentry/components/errorBoundary'; import {StreamlinedExternalIssueList} from 'sentry/components/group/externalIssuesList/streamlinedExternalIssueList'; +import * as SidebarSection from 'sentry/components/sidebarSection'; import {space} from 'sentry/styles/space'; import type {Event} from 'sentry/types/event'; import type {Group} from 'sentry/types/group'; import type {Project} from 'sentry/types/project'; import StreamlinedActivitySection from 'sentry/views/issueDetails/streamline/activitySection'; import FirstLastSeenSection from 'sentry/views/issueDetails/streamline/firstLastSeenSection'; +import PeopleSection from 'sentry/views/issueDetails/streamline/peopleSection'; type Props = { group: Group; @@ -27,6 +29,8 @@ export default function StreamlinedSidebar({group, event, project}: Props) { )} + + ); } @@ -36,3 +40,8 @@ const StyledBreak = styled('hr')` margin-bottom: ${space(1.5)}; border-color: ${p => p.theme.border}; `; + +export const SidebarSectionTitle = styled(SidebarSection.Title)` + margin-bottom: ${space(1)}; + color: ${p => p.theme.headingColor}; +`;