selectorTable.tsx 3.1 KB

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