import {createPortal} from 'react-dom';
import {Link} from 'react-router';
import {useTheme} from '@emotion/react';
import styled from '@emotion/styled';
import {AnimatePresence} from 'framer-motion';
import * as qs from 'query-string';
import {CodeSnippet} from 'sentry/components/codeSnippet';
import LoadingIndicator from 'sentry/components/loadingIndicator';
import {Overlay, PositionWrapper} from 'sentry/components/overlay';
import {space} from 'sentry/styles/space';
import {useHoverOverlay, UseHoverOverlayProps} from 'sentry/utils/useHoverOverlay';
import {useLocation} from 'sentry/utils/useLocation';
import {OverflowEllipsisTextContainer} from 'sentry/views/starfish/components/textAlign';
import {useFullSpanFromTrace} from 'sentry/views/starfish/queries/useFullSpanFromTrace';
import {ModuleName, StarfishFunctions} from 'sentry/views/starfish/types';
import {extractRoute} from 'sentry/views/starfish/utils/extractRoute';
import {SQLishFormatter} from 'sentry/views/starfish/utils/sqlish/SQLishFormatter';
import {QueryParameterNames} from 'sentry/views/starfish/views/queryParameters';
interface Props {
moduleName: ModuleName;
projectId: number;
description?: string;
endpoint?: string;
endpointMethod?: string;
group?: string;
}
const formatter = new SQLishFormatter();
export function SpanDescriptionCell({
description,
group,
moduleName,
endpoint,
endpointMethod,
projectId,
}: Props) {
const location = useLocation();
const hoverOverlayProps = useHoverOverlay('overlay', OVERLAY_OPTIONS);
if (!description) {
return NULL_DESCRIPTION;
}
const queryString = {
...location.query,
project: projectId,
endpoint,
endpointMethod,
};
const sort: string | undefined = queryString[QueryParameterNames.SORT];
// the spans page uses time_spent_percentage(local), so to persist the sort upon navigation we need to replace
if (sort?.includes(`${StarfishFunctions.TIME_SPENT_PERCENTAGE}()`)) {
queryString[QueryParameterNames.SORT] = sort.replace(
`${StarfishFunctions.TIME_SPENT_PERCENTAGE}()`,
`${StarfishFunctions.TIME_SPENT_PERCENTAGE}(local)`
);
}
const formattedDescription =
moduleName === ModuleName.DB ? formatter.toSimpleMarkup(description) : description;
const overlayContent = moduleName === ModuleName.DB && hoverOverlayProps.isOpen && (
);
return (
{hoverOverlayProps.wrapTrigger(
{group ? (
{formattedDescription}
) : (
formattedDescription
)}
)}
{createPortal({overlayContent}, document.body)}
);
}
const DescriptionWrapper = styled('div')`
display: inline-flex;
`;
const OVERLAY_OPTIONS: UseHoverOverlayProps = {
position: 'right',
isHoverable: true,
skipWrapper: true,
};
const NULL_DESCRIPTION = <null>;
interface QueryDescriptionOverlayProps {
hoverOverlayProps: ReturnType;
group?: string;
shortDescription?: string;
}
function QueryDescriptionOverlay({
group,
shortDescription,
hoverOverlayProps,
}: QueryDescriptionOverlayProps) {
const theme = useTheme();
const {
data: fullSpan,
isLoading,
isFetching,
} = useFullSpanFromTrace(group, Boolean(group));
const description = fullSpan?.description ?? shortDescription;
return description ? (
{/* N.B. A `disabled` query still returns `isLoading: true`, so we also
check the fetching status explicitly. */}
{isLoading && isFetching ? (
) : (
{formatter.toString(description)}
)}
) : null;
}
const OverlayContent = styled(Overlay)`
max-width: 500px;
`;
const PaddedSpinner = styled('div')`
padding: ${space(1)};
`;