123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194 |
- // 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 (
- <GlobalSelectionLink {...props}>
- <VersionText truncate={truncate}>{versionToDisplay}</VersionText>
- </GlobalSelectionLink>
- );
- }
- return (
- <Link {...props}>
- <VersionText truncate={truncate}>{versionToDisplay}</VersionText>
- </Link>
- );
- }
- return (
- <VersionText className={className} truncate={truncate}>
- {versionToDisplay}
- </VersionText>
- );
- };
- const renderTooltipContent = () => (
- <TooltipContent
- onClick={e => {
- e.stopPropagation();
- }}
- >
- <TooltipVersionWrapper>{version}</TooltipVersionWrapper>
- <Clipboard value={version}>
- <TooltipClipboardIconWrapper>
- <IconCopy size="xs" />
- </TooltipClipboardIconWrapper>
- </Clipboard>
- </TooltipContent>
- );
- 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 (
- <Tooltip
- title={renderTooltipContent()}
- disabled={!tooltipRawVersion}
- isHoverable
- containerDisplayMode={truncate ? 'block' : 'inline-block'}
- overlayStyle={getOverlayStyle()}
- >
- {renderVersion()}
- </Tooltip>
- );
- };
- // 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<Props, 'organization'>;
- export default withOrganization(
- withRouter(Version)
- ) as React.ComponentClass<PropsWithoutOrg>;
|