import {Fragment, PureComponent} from 'react'; import styled from '@emotion/styled'; import {Location, LocationDescriptor, Query} from 'history'; import SortLink from 'sentry/components/gridEditable/sortLink'; import Link from 'sentry/components/links/link'; import LoadingIndicator from 'sentry/components/loadingIndicator'; import PanelTable from 'sentry/components/panels/panelTable'; import QuestionTooltip from 'sentry/components/questionTooltip'; import {t} from 'sentry/locale'; import space from 'sentry/styles/space'; import {Organization} from 'sentry/types'; import {TableData, TableDataRow} from 'sentry/utils/discover/discoverQuery'; import EventView, {MetaType} from 'sentry/utils/discover/eventView'; import {getFieldRenderer} from 'sentry/utils/discover/fieldRenderers'; import { Alignments, fieldAlignment, getAggregateAlias, } from 'sentry/utils/discover/fields'; import {VisuallyCompleteWithData} from 'sentry/utils/performanceForSentry'; import CellAction, {Actions} from 'sentry/views/eventsV2/table/cellAction'; import {TableColumn} from 'sentry/views/eventsV2/table/types'; import {GridCell, GridCellNumber} from 'sentry/views/performance/styles'; import {TrendsDataEvents} from 'sentry/views/performance/trends/types'; type Props = { columnOrder: TableColumn[]; eventView: EventView; isLoading: boolean; location: Location; organization: Organization; tableData: TableData | TrendsDataEvents | null; useAggregateAlias: boolean; generateLink?: Record< string, ( organization: Organization, tableRow: TableDataRow, query: Query ) => LocationDescriptor >; handleCellAction?: ( c: TableColumn ) => (a: Actions, v: React.ReactText) => void; titles?: string[]; }; class TransactionsTable extends PureComponent { getTitles() { const {eventView, titles} = this.props; return titles ?? eventView.getFields(); } renderHeader() { const {tableData, columnOrder} = this.props; const tableMeta = tableData?.meta; const generateSortLink = () => undefined; const tableTitles = this.getTitles(); const headers = tableTitles.map((title, index) => { const column = columnOrder[index]; const align: Alignments = fieldAlignment(column.name, column.type, tableMeta); if (column.key === 'span_ops_breakdown.relative') { return ( {title} ) : ( title ) } direction={undefined} canSort={false} generateSortLink={generateSortLink} /> ); } return ( ); }); return headers; } renderRow( row: TableDataRow, rowIndex: number, columnOrder: TableColumn[], tableMeta: MetaType ): React.ReactNode[] { const { eventView, organization, location, generateLink, handleCellAction, titles, useAggregateAlias, } = this.props; const fields = eventView.getFields(); if (titles && titles.length) { // Slice to match length of given titles columnOrder = columnOrder.slice(0, titles.length); } const resultsRow = columnOrder.map((column, index) => { const field = String(column.key); // TODO add a better abstraction for this in fieldRenderers. const fieldName = useAggregateAlias ? getAggregateAlias(field) : field; const fieldType = tableMeta[fieldName]; const fieldRenderer = getFieldRenderer(field, tableMeta, useAggregateAlias); let rendered = fieldRenderer(row, {organization, location}); const target = generateLink?.[field]?.(organization, row, location.query); if (target) { rendered = ( {rendered} ); } const isNumeric = ['integer', 'number', 'duration'].includes(fieldType); const key = `${rowIndex}:${column.key}:${index}`; rendered = isNumeric ? ( {rendered} ) : ( {rendered} ); if (handleCellAction) { rendered = ( {rendered} ); } return {rendered}; }); return resultsRow; } renderResults() { const {isLoading, tableData, columnOrder} = this.props; let cells: React.ReactNode[] = []; if (isLoading) { return cells; } if (!tableData || !tableData.meta || !tableData.data) { return cells; } tableData.data.forEach((row, i: number) => { // Another check to appease tsc if (!tableData.meta) { return; } cells = cells.concat(this.renderRow(row, i, columnOrder, tableData.meta)); }); return cells; } render() { const {isLoading, tableData} = this.props; const hasResults = tableData && tableData.data && tableData.meta && tableData.data.length > 0; // Custom set the height so we don't have layout shift when results are loaded. const loader = ; return ( {this.renderResults()} ); } } const HeadCellContainer = styled('div')` padding: ${space(2)}; `; const BodyCellContainer = styled('div')` padding: ${space(1)} ${space(2)}; ${p => p.theme.overflowEllipsis}; `; const StyledIconQuestion = styled(QuestionTooltip)` position: relative; top: 1px; left: 4px; `; export default TransactionsTable;