// eslint-disable-next-line no-restricted-imports import {withRouter, WithRouterProps} from 'react-router'; import {css} from '@emotion/react'; import styled from '@emotion/styled'; import Clipboard from 'sentry/components/clipboard'; import GlobalSelectionLink from 'sentry/components/globalSelectionLink'; import Link from 'sentry/components/links/link'; import Tooltip from 'sentry/components/tooltip'; import {IconCopy} from 'sentry/icons'; import space from 'sentry/styles/space'; import {Organization} from 'sentry/types'; import {formatVersion} from 'sentry/utils/formatters'; import theme from 'sentry/utils/theme'; import withOrganization from 'sentry/utils/withOrganization'; type Props = { /** * Organization injected by withOrganization HOC */ organization: Organization; /** * Raw version (canonical release identifier) */ version: string; /** * Should the version be a link to the release page */ anchor?: boolean; className?: string; /** * Should link to release page preserve user's page filter values */ preservePageFilters?: boolean; /** * Will add project ID to the linked url (can be overridden by preservePageFilters). * If not provided and user does not have global-views enabled, it will try to take it from current url query. */ projectId?: string; /** * Should there be a tooltip with raw version on hover */ tooltipRawVersion?: boolean; /** * Ellipsis on overflow */ truncate?: boolean; /** * Should we also show package name */ withPackage?: boolean; }; const Version = ({ version, organization, anchor = true, preservePageFilters, tooltipRawVersion, withPackage, projectId, truncate, className, location, }: WithRouterProps & Props) => { const versionToDisplay = formatVersion(version, withPackage); let releaseDetailProjectId: null | undefined | string | string[]; if (projectId) { // we can override preservePageFilters's project id releaseDetailProjectId = projectId; } else if (!organization?.features.includes('global-views')) { // we need this for users without global-views, otherwise they might get `This release may not be in your selected project` releaseDetailProjectId = location?.query.project; } const renderVersion = () => { if (anchor && organization?.slug) { const props = { to: { pathname: `/organizations/${organization?.slug}/releases/${encodeURIComponent( version )}/`, query: releaseDetailProjectId ? {project: releaseDetailProjectId} : undefined, }, className, }; if (preservePageFilters) { return ( {versionToDisplay} ); } return ( {versionToDisplay} ); } return ( {versionToDisplay} ); }; const renderTooltipContent = () => ( { e.stopPropagation(); }} > {version} ); const getOverlayStyle = () => { // if the version name is not a hash (sha1 or sha265) and we are not on // mobile, allow tooltip to be as wide as 500px if (/(^[a-f0-9]{40}$)|(^[a-f0-9]{64}$)/.test(version)) { return undefined; } return css` @media (min-width: ${theme.breakpoints.small}) { max-width: 500px; } `; }; return ( {renderVersion()} ); }; // TODO(matej): try to wrap version with this when truncate prop is true (in separate PR) // const VersionWrapper = styled('div')` // ${p => p.theme.overflowEllipsis}; // max-width: 100%; // width: auto; // display: inline-block; // `; const VersionText = styled('span')<{truncate?: boolean}>` ${p => p.truncate && `max-width: 100%; display: block; overflow: hidden; font-variant-numeric: tabular-nums; text-overflow: ellipsis; white-space: nowrap;`} `; const TooltipContent = styled('span')` display: flex; align-items: center; `; const TooltipVersionWrapper = styled('span')` ${p => p.theme.overflowEllipsis} `; const TooltipClipboardIconWrapper = styled('span')` margin-left: ${space(0.5)}; position: relative; bottom: -${space(0.25)}; &:hover { cursor: pointer; } `; type PropsWithoutOrg = Omit; export default withOrganization( withRouter(Version) ) as React.ComponentClass;