simpleTableChart.tsx 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149
  1. import {Fragment} from 'react';
  2. import styled from '@emotion/styled';
  3. import {Location} from 'history';
  4. import PanelTable, {
  5. PanelTableHeader,
  6. PanelTableProps,
  7. } from 'sentry/components/panels/panelTable';
  8. import Tooltip from 'sentry/components/tooltip';
  9. import Truncate from 'sentry/components/truncate';
  10. import space from 'sentry/styles/space';
  11. import {Organization} from 'sentry/types';
  12. import {TableData, TableDataRow} from 'sentry/utils/discover/discoverQuery';
  13. import EventView, {MetaType} from 'sentry/utils/discover/eventView';
  14. import {getFieldRenderer} from 'sentry/utils/discover/fieldRenderers';
  15. import {fieldAlignment} from 'sentry/utils/discover/fields';
  16. import withOrganization from 'sentry/utils/withOrganization';
  17. import {ContextualProps} from 'sentry/views/dashboardsV2/datasetConfig/base';
  18. import TopResultsIndicator from 'sentry/views/eventsV2/table/topResultsIndicator';
  19. import {decodeColumnOrder} from 'sentry/views/eventsV2/utils';
  20. type Props = {
  21. eventView: EventView;
  22. fieldAliases: string[];
  23. fields: string[];
  24. loading: boolean;
  25. location: Location;
  26. organization: Organization;
  27. title: string;
  28. className?: string;
  29. data?: TableData['data'];
  30. fieldHeaderMap?: Record<string, string>;
  31. getCustomFieldRenderer?: (
  32. field: string,
  33. meta: MetaType,
  34. contextualProps?: ContextualProps
  35. ) => ReturnType<typeof getFieldRenderer> | null;
  36. loader?: PanelTableProps['loader'];
  37. metadata?: TableData['meta'];
  38. stickyHeaders?: boolean;
  39. topResultsIndicators?: number;
  40. };
  41. function SimpleTableChart({
  42. className,
  43. loading,
  44. eventView,
  45. fields,
  46. metadata,
  47. data,
  48. title,
  49. fieldHeaderMap,
  50. stickyHeaders,
  51. getCustomFieldRenderer,
  52. organization,
  53. topResultsIndicators,
  54. location,
  55. fieldAliases,
  56. loader,
  57. }: Props) {
  58. function renderRow(
  59. index: number,
  60. row: TableDataRow,
  61. tableMeta: NonNullable<TableData['meta']>,
  62. columns: ReturnType<typeof decodeColumnOrder>
  63. ) {
  64. return columns.map((column, columnIndex) => {
  65. const fieldRenderer =
  66. getCustomFieldRenderer?.(column.key, tableMeta, {organization}) ??
  67. getFieldRenderer(column.key, tableMeta);
  68. return (
  69. <TableCell key={`${index}-${columnIndex}:${column.name}`}>
  70. {topResultsIndicators && columnIndex === 0 && (
  71. <TopResultsIndicator count={topResultsIndicators} index={index} />
  72. )}
  73. {fieldRenderer(row, {organization, location, eventView})}
  74. </TableCell>
  75. );
  76. });
  77. }
  78. const meta = metadata ?? {};
  79. const usingEvents = organization.features.includes(
  80. 'discover-frontend-use-events-endpoint'
  81. );
  82. const columns = decodeColumnOrder(
  83. fields.map((field, index) => ({field, alias: fieldAliases[index]})),
  84. usingEvents
  85. );
  86. return (
  87. <Fragment>
  88. {title && <h4>{title}</h4>}
  89. <StyledPanelTable
  90. className={className}
  91. isLoading={loading}
  92. loader={loader}
  93. headers={columns.map((column, index) => {
  94. const align = fieldAlignment(column.name, column.type, meta);
  95. const header =
  96. column.column.alias || (fieldHeaderMap?.[column.key] ?? column.name);
  97. return (
  98. <HeadCell key={index} align={align}>
  99. <Tooltip title={header}>
  100. <StyledTruncate value={header} maxLength={30} expandable={false} />
  101. </Tooltip>
  102. </HeadCell>
  103. );
  104. })}
  105. isEmpty={!data?.length}
  106. stickyHeaders={stickyHeaders}
  107. disablePadding
  108. >
  109. {data?.map((row, index) => renderRow(index, row, meta, columns))}
  110. </StyledPanelTable>
  111. </Fragment>
  112. );
  113. }
  114. const StyledTruncate = styled(Truncate)`
  115. white-space: nowrap;
  116. `;
  117. const StyledPanelTable = styled(PanelTable)`
  118. border-radius: 0;
  119. border-left: 0;
  120. border-right: 0;
  121. border-bottom: 0;
  122. margin: 0;
  123. ${/* sc-selector */ PanelTableHeader} {
  124. height: min-content;
  125. }
  126. `;
  127. type HeadCellProps = {
  128. align: string | undefined;
  129. };
  130. const HeadCell = styled('div')<HeadCellProps>`
  131. ${(p: HeadCellProps) => (p.align ? `text-align: ${p.align};` : '')}
  132. padding: ${space(1)} ${space(3)};
  133. `;
  134. export const TableCell = styled('div')`
  135. padding: ${space(1)} ${space(3)};
  136. `;
  137. export default withOrganization(SimpleTableChart);