selectorTable.tsx 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149
  1. import {Fragment, ReactNode, useCallback, useMemo} from 'react';
  2. import styled from '@emotion/styled';
  3. import type {Location} from 'history';
  4. import renderSortableHeaderCell from 'sentry/components/feedback/table/renderSortableHeaderCell';
  5. import useQueryBasedColumnResize from 'sentry/components/feedback/table/useQueryBasedColumnResize';
  6. import useQueryBasedSorting from 'sentry/components/feedback/table/useQueryBasedSorting';
  7. import GridEditable, {GridColumnOrder} from 'sentry/components/gridEditable';
  8. import Link from 'sentry/components/links/link';
  9. import TextOverflow from 'sentry/components/textOverflow';
  10. import {Organization} from 'sentry/types';
  11. import useOrganization from 'sentry/utils/useOrganization';
  12. import {normalizeUrl} from 'sentry/utils/withDomainRequired';
  13. import {DeadRageSelectorItem} from 'sentry/views/replays/types';
  14. export interface UrlState {
  15. widths: string[];
  16. }
  17. interface Props {
  18. clickCountColumn: {key: string; name: string};
  19. clickCountSortable: boolean;
  20. data: DeadRageSelectorItem[];
  21. isError: boolean;
  22. isLoading: boolean;
  23. location: Location<any>;
  24. customHandleResize?: () => void;
  25. headerButtons?: ReactNode;
  26. title?: ReactNode;
  27. }
  28. const BASE_COLUMNS: GridColumnOrder<string>[] = [
  29. {key: 'element', name: 'element'},
  30. {key: 'dom_element', name: 'selector'},
  31. {key: 'aria_label', name: 'aria label'},
  32. ];
  33. export default function SelectorTable({
  34. clickCountColumn,
  35. clickCountSortable,
  36. data,
  37. isError,
  38. isLoading,
  39. location,
  40. title,
  41. headerButtons,
  42. customHandleResize,
  43. }: Props) {
  44. const organization = useOrganization();
  45. const {currentSort, makeSortLinkGenerator} = useQueryBasedSorting({
  46. defaultSort: {field: clickCountColumn.key, kind: 'desc'},
  47. location,
  48. });
  49. const {columns, handleResizeColumn} = useQueryBasedColumnResize({
  50. columns: BASE_COLUMNS.concat(clickCountColumn),
  51. location,
  52. });
  53. const renderHeadCell = useMemo(
  54. () =>
  55. renderSortableHeaderCell({
  56. currentSort,
  57. makeSortLinkGenerator,
  58. onClick: () => {},
  59. rightAlignedColumns: [],
  60. sortableColumns: clickCountSortable ? [clickCountColumn] : [],
  61. }),
  62. [clickCountColumn, currentSort, makeSortLinkGenerator, clickCountSortable]
  63. );
  64. const renderBodyCell = useCallback(
  65. (column, dataRow) => {
  66. const value = dataRow[column.key];
  67. switch (column.key) {
  68. case 'dom_element':
  69. return <SelectorLink organization={organization} value={value} />;
  70. case 'element':
  71. case 'aria_label':
  72. return (
  73. <code>
  74. <TextOverflow>{value}</TextOverflow>
  75. </code>
  76. );
  77. default:
  78. return renderSimpleBodyCell<DeadRageSelectorItem>(column, dataRow);
  79. }
  80. },
  81. [organization]
  82. );
  83. return (
  84. <Fragment>
  85. <GridEditable
  86. error={isError}
  87. isLoading={isLoading}
  88. data={data ?? []}
  89. columnOrder={columns}
  90. columnSortBy={[]}
  91. stickyHeader
  92. grid={{
  93. onResizeColumn: customHandleResize ?? handleResizeColumn,
  94. renderHeadCell,
  95. renderBodyCell,
  96. }}
  97. location={location as Location<any>}
  98. title={title}
  99. headerButtons={() => headerButtons}
  100. />
  101. </Fragment>
  102. );
  103. }
  104. function SelectorLink({
  105. organization,
  106. value,
  107. }: {
  108. organization: Organization;
  109. value: string;
  110. }) {
  111. return (
  112. <Link
  113. to={{
  114. pathname: normalizeUrl(`/organizations/${organization.slug}/replays/`),
  115. }}
  116. >
  117. <TextOverflow>{value}</TextOverflow>
  118. </Link>
  119. );
  120. }
  121. function renderSimpleBodyCell<T>(column: GridColumnOrder<string>, dataRow: T) {
  122. if (column.key === 'count_dead_clicks') {
  123. return <DeadClickCount>{dataRow[column.key]}</DeadClickCount>;
  124. }
  125. if (column.key === 'count_rage_clicks') {
  126. return <RageClickCount>{dataRow[column.key]}</RageClickCount>;
  127. }
  128. return <TextOverflow>{dataRow[column.key]}</TextOverflow>;
  129. }
  130. const DeadClickCount = styled(TextOverflow)`
  131. color: ${p => p.theme.yellow300};
  132. `;
  133. const RageClickCount = styled(TextOverflow)`
  134. color: ${p => p.theme.red300};
  135. `;