import {Fragment} from 'react'; import styled from '@emotion/styled'; import AlertLink from 'sentry/components/alertLink'; import {Button, type ButtonProps, LinkButton} from 'sentry/components/button'; import DropdownButton from 'sentry/components/dropdownButton'; import {DropdownMenu} from 'sentry/components/dropdownMenu'; import ErrorBoundary from 'sentry/components/errorBoundary'; import type {ExternalIssueAction} from 'sentry/components/group/externalIssuesList/hooks/types'; import useGroupExternalIssues from 'sentry/components/group/externalIssuesList/hooks/useGroupExternalIssues'; import Placeholder from 'sentry/components/placeholder'; import * as SidebarSection from 'sentry/components/sidebarSection'; import {Tooltip} from 'sentry/components/tooltip'; import {t} from 'sentry/locale'; 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 useOrganization from 'sentry/utils/useOrganization'; import {SidebarSectionTitle} from 'sentry/views/issueDetails/streamline/sidebar/sidebar'; function getActionLabelAndTextValue({ action, integrationDisplayName, }: { action: ExternalIssueAction; integrationDisplayName: string; }): {label: string | JSX.Element; textValue: string} { // If there's no subtext or subtext matches name, just show name if (!action.nameSubText || action.nameSubText === action.name) { return { label: action.name, textValue: action.name, }; } // If action name matches integration name, just show subtext if (action.name === integrationDisplayName) { return { label: action.nameSubText, textValue: `${action.name} ${action.nameSubText}`, }; } // Otherwise show both name and subtext return { label: (
{action.name}
{action.nameSubText}
), textValue: `${action.name} ${action.nameSubText}`, }; } interface ExternalIssueListProps { event: Event; group: Group; project: Project; } export function ExternalIssueList({group, event, project}: ExternalIssueListProps) { const organization = useOrganization(); const {isLoading, integrations, linkedIssues} = useGroupExternalIssues({ group, event, project, }); if (isLoading) { return (
{t('Issue Tracking')}
); } return (
{t('Issue Tracking')} {integrations.length || linkedIssues.length ? ( {linkedIssues.map(linkedIssue => ( {linkedIssue.title} {t('Unlink issue')} } isHoverable > {linkedIssue.displayName} ))} {integrations.map(integration => { const sharedButtonProps: ButtonProps = { size: 'zero', icon: integration.displayIcon, children: {integration.displayName}, }; if (integration.actions.length === 1) { return ( ); } return ( ( )} items={integration.actions.map(action => ({ key: action.id, ...getActionLabelAndTextValue({ action, integrationDisplayName: integration.displayName, }), onAction: action.onClick, disabled: integration.disabled, }))} /> ); })} ) : ( {t('Track this issue in Jira, GitHub, etc.')} )}
); } const IssueActionWrapper = styled('div')` display: flex; flex-wrap: wrap; gap: ${space(1)}; line-height: 1.2; &:not(:last-child) { margin-bottom: ${space(1)}; } `; const LinkedIssue = styled(LinkButton)` display: flex; align-items: center; padding: ${space(0.5)} ${space(0.75)}; border: 1px solid ${p => p.theme.border}; border-radius: ${p => p.theme.borderRadius}; font-weight: normal; `; const IssueActionButton = styled(Button)` display: flex; align-items: center; padding: ${space(0.5)} ${space(0.75)}; border: 1px dashed ${p => p.theme.border}; border-radius: ${p => p.theme.borderRadius}; font-weight: normal; `; const IssueActionDropdownMenu = styled(DropdownButton)` display: flex; align-items: center; padding: ${space(0.5)} ${space(0.75)}; border: 1px dashed ${p => p.theme.border}; border-radius: ${p => p.theme.borderRadius}; font-weight: normal; &[aria-expanded='true'] { border: 1px solid ${p => p.theme.border}; } `; const IssueActionName = styled('div')` ${p => p.theme.overflowEllipsis} max-width: 200px; `; const LinkedIssueTooltipWrapper = styled('div')` display: flex; align-items: center; gap: ${space(0.5)}; white-space: nowrap; `; const LinkedIssueName = styled('div')` ${p => p.theme.overflowEllipsis} margin-right: ${space(0.25)}; `; const HorizontalSeparator = styled('div')` width: 1px; height: 14px; background: ${p => p.theme.border}; `; const UnlinkButton = styled(Button)` color: ${p => p.theme.subText}; `;