import {Component} from 'react';
import styled from '@emotion/styled';
import capitalize from 'lodash/capitalize';
import Button from 'sentry/components/button';
import ButtonBar from 'sentry/components/buttonBar';
import KeyValueList from 'sentry/components/events/interfaces/keyValueList';
import QuestionTooltip from 'sentry/components/questionTooltip';
import Tooltip from 'sentry/components/tooltip';
import {IconCheckmark, IconClose} from 'sentry/icons';
import {t} from 'sentry/locale';
import space from 'sentry/styles/space';
import {
EventGroupComponent,
EventGroupVariant,
EventGroupVariantType,
} from 'sentry/types';
import GroupingComponent from './groupingComponent';
import {hasNonContributingComponent} from './utils';
type Props = {
showGroupingConfig: boolean;
variant: EventGroupVariant;
};
type State = {
showNonContributing: boolean;
};
type VariantData = [string, React.ReactNode][];
function addFingerprintInfo(data: VariantData, variant: EventGroupVariant) {
if ('matched_rule' in variant) {
data.push([
t('Fingerprint rule'),
{variant.matched_rule}
,
]);
}
if ('values' in variant) {
data.push([t('Fingerprint values'), variant.values]);
}
if ('client_values' in variant) {
data.push([
t('Client fingerprint values'),
{variant.client_values}
,
]);
}
}
class GroupVariant extends Component {
state: State = {
showNonContributing: false,
};
handleShowNonContributing = () => {
this.setState({showNonContributing: true});
};
handleHideNonContributing = () => {
this.setState({showNonContributing: false});
};
getVariantData(): [VariantData, EventGroupComponent | undefined] {
const {variant, showGroupingConfig} = this.props;
const data: VariantData = [];
let component: EventGroupComponent | undefined;
if (!this.state.showNonContributing && variant.hash === null) {
return [data, component];
}
if (variant.hash !== null) {
data.push([
t('Hash'),
{variant.hash}
,
]);
}
if (variant.hashMismatch) {
data.push([
t('Hash mismatch'),
t('hashing algorithm produced a hash that does not match the event'),
]);
}
switch (variant.type) {
case EventGroupVariantType.COMPONENT:
component = variant.component;
data.push([
t('Type'),
{variant.type}
,
]);
if (showGroupingConfig && variant.config?.id) {
data.push([t('Grouping Config'), variant.config.id]);
}
break;
case EventGroupVariantType.CUSTOM_FINGERPRINT:
data.push([
t('Type'),
{variant.type}
,
]);
addFingerprintInfo(data, variant);
break;
case EventGroupVariantType.SALTED_COMPONENT:
component = variant.component;
data.push([
t('Type'),
{variant.type}
,
]);
addFingerprintInfo(data, variant);
if (showGroupingConfig && variant.config?.id) {
data.push([t('Grouping Config'), variant.config.id]);
}
break;
case EventGroupVariantType.PERFORMANCE_PROBLEM:
data.push([
t('Type'),
{variant.type}
,
]);
data.push(['Performance Issue Type', variant.key]);
data.push(['Span Operation', variant.evidence.op]);
data.push(['Parent Span Hashes', variant.evidence.parent_span_hashes]);
data.push(['Source Span Hashes', variant.evidence.cause_span_hashes]);
data.push([
'Offender Span Hashes',
[...new Set(variant.evidence.offender_span_hashes)],
]);
break;
default:
break;
}
if (component) {
data.push([
t('Grouping'),
,
]);
}
return [data, component];
}
renderTitle() {
const {variant} = this.props;
const isContributing = variant.hash !== null;
let title: string;
if (isContributing) {
title = t('Contributing variant');
} else {
const hint = 'component' in variant ? variant.component?.hint : undefined;
if (hint) {
title = t('Non-contributing variant: %s', hint);
} else {
title = t('Non-contributing variant');
}
}
return (
{t('By')}{' '}
{variant.description
?.split(' ')
.map(i => capitalize(i))
.join(' ') ?? t('Nothing')}
);
}
renderContributionToggle() {
const {showNonContributing} = this.state;
return (
);
}
render() {
const [data, component] = this.getVariantData();
return (
{this.renderTitle()}
{hasNonContributingComponent(component) && this.renderContributionToggle()}
({
key: d[0],
subject: d[0],
value: d[1],
}))}
isContextData
isSorted={false}
/>
);
}
}
const VariantWrapper = styled('div')`
margin-bottom: ${space(4)};
`;
const Header = styled('div')`
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: ${space(2)};
@media (max-width: ${p => p.theme.breakpoints.small}) {
display: block;
}
`;
const VariantTitle = styled('h5')`
font-size: ${p => p.theme.fontSizeMedium};
margin: 0;
display: flex;
align-items: center;
`;
const ContributionIcon = styled(({isContributing, ...p}) =>
isContributing ? (
) : (
)
)`
margin-right: ${space(1)};
`;
const ContributingToggle = styled(ButtonBar)`
justify-content: flex-end;
@media (max-width: ${p => p.theme.breakpoints.small}) {
margin-top: ${space(0.5)};
}
`;
const GroupingTree = styled('div')`
color: ${p => p.theme.textColor};
`;
const TextWithQuestionTooltip = styled('div')`
display: grid;
align-items: center;
grid-template-columns: auto 1fr;
gap: ${space(0.5)};
`;
const Hash = styled('span')`
@media (max-width: ${p => p.theme.breakpoints.small}) {
${p => p.theme.overflowEllipsis};
width: 210px;
}
`;
export default GroupVariant;