import * as React from 'react'; import isPropValid from '@emotion/is-prop-valid'; import styled from '@emotion/styled'; import {LocationDescriptor} from 'history'; import {TagSegment} from 'sentry/actionCreators/events'; import Link from 'sentry/components/links/link'; import Tooltip from 'sentry/components/tooltip'; import Version from 'sentry/components/version'; import {t} from 'sentry/locale'; import overflowEllipsis from 'sentry/styles/overflowEllipsis'; import space from 'sentry/styles/space'; import {percent} from 'sentry/utils'; type DefaultProps = { hasError: boolean; isLoading: boolean; renderEmpty: () => React.ReactNode; renderError: () => React.ReactNode; renderLoading: () => React.ReactNode; showReleasePackage: boolean; }; type Props = DefaultProps & { segments: TagSegment[]; title: string; totalValues: number; onTagClick?: (title: string, value: TagSegment) => void; }; type SegmentValue = { index: number; onClick: () => void; to: LocationDescriptor; }; export default class TagDistributionMeter extends React.Component { static defaultProps: DefaultProps = { isLoading: false, hasError: false, renderLoading: () => null, renderEmpty: () =>

{t('No recent data.')}

, renderError: () => null, showReleasePackage: false, }; renderTitle() { const {segments, totalValues, title, isLoading, hasError, showReleasePackage} = this.props; if (!Array.isArray(segments) || segments.length <= 0) { return ( <TitleType>{title}</TitleType> ); } const largestSegment = segments[0]; const pct = percent(largestSegment.count, totalValues); const pctLabel = Math.floor(pct); const renderLabel = () => { switch (title) { case 'release': return ( ); default: return ; } }; return ( <TitleType>{title}</TitleType> <TitleDescription> {renderLabel()} {isLoading || hasError ? null : <Percent>{pctLabel}%</Percent>} </TitleDescription> ); } renderSegments() { const { segments, onTagClick, title, isLoading, hasError, totalValues, renderLoading, renderError, renderEmpty, showReleasePackage, } = this.props; if (isLoading) { return renderLoading(); } if (hasError) { return {renderError()}; } if (totalValues === 0) { return {renderEmpty()}; } return ( {segments.map((value, index) => { const pct = percent(value.count, totalValues); const pctLabel = Math.floor(pct); const renderTooltipValue = () => { switch (title) { case 'release': return ( ); default: return value.name || t('n/a'); } }; const tooltipHtml = (
{renderTooltipValue()}
{pctLabel}%
); const segmentProps: SegmentValue = { index, to: value.url, onClick: () => { onTagClick?.(title, value); }, }; return (
{value.isOther ? ( ) : ( )}
); })}
); } render() { const {segments, totalValues} = this.props; const totalVisible = segments.reduce((sum, value) => sum + value.count, 0); const hasOther = totalVisible < totalValues; if (hasOther) { segments.push({ isOther: true, name: t('Other'), value: 'other', count: totalValues - totalVisible, url: '', }); } return ( {this.renderTitle()} {this.renderSegments()} ); } } const COLORS = [ '#3A3387', '#5F40A3', '#8C4FBD', '#B961D3', '#DE76E4', '#EF91E8', '#F7B2EC', '#FCD8F4', '#FEEBF9', ]; const TagSummary = styled('div')` margin-bottom: ${space(1)}; `; const SegmentBar = styled('div')` display: flex; overflow: hidden; border-radius: 2px; `; const Title = styled('div')` display: flex; font-size: ${p => p.theme.fontSizeSmall}; justify-content: space-between; `; const TitleType = styled('div')` color: ${p => p.theme.textColor}; font-weight: bold; ${overflowEllipsis}; `; const TitleDescription = styled('div')` display: flex; color: ${p => p.theme.gray300}; text-align: right; `; const Label = styled('div')` ${overflowEllipsis}; max-width: 150px; `; const Percent = styled('div')` font-weight: bold; font-variant-numeric: tabular-nums; padding-left: ${space(0.5)}; color: ${p => p.theme.textColor}; `; const OtherSegment = styled('span')` display: block; width: 100%; height: 16px; color: inherit; outline: none; background-color: ${COLORS[COLORS.length - 1]}; `; const Segment = styled(Link, {shouldForwardProp: isPropValid})` display: block; width: 100%; height: 16px; color: inherit; outline: none; background-color: ${p => COLORS[p.index]}; `;