import {Fragment} from 'react'; import styled from '@emotion/styled'; import {Button, type ButtonProps, LinkButton} from 'sentry/components/button'; import {AlertLink} from 'sentry/components/core/alert/alertLink'; 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 {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 {SectionKey} from 'sentry/views/issueDetails/streamline/context'; import {SidebarFoldSection} from 'sentry/views/issueDetails/streamline/foldSection'; 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, }); const hasLinkedIssuesOrIntegrations = integrations.length || linkedIssues.length; return ( {t('Issue Tracking')}} sectionKey={SectionKey.EXTERNAL_ISSUES} > {isLoading ? ( ) : hasLinkedIssuesOrIntegrations ? ( {linkedIssues.length > 0 && ( {linkedIssues.map(linkedIssue => ( {linkedIssue.title} {t('Unlink issue')} } isHoverable > {linkedIssue.displayName} ))} )} {integrations.length > 0 && ( {integrations.map(integration => { const sharedButtonProps: ButtonProps = { size: 'zero', icon: integration.displayIcon, children: {integration.displayName}, }; if (integration.actions.length === 1) { const action = integration.actions[0]!; return ( {action.href ? ( // Exclusively used for group.pluginActions {integration.displayName} ) : ( )} ); } 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 Title = styled('div')` font-size: ${p => p.theme.fontSizeMedium}; `; 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 IssueActionLinkButton = styled(LinkButton)` 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}; `;