import {ReactNode, useCallback, useMemo} from 'react'; import styled from '@emotion/styled'; import type {Location} from 'history'; import renderSortableHeaderCell from 'sentry/components/feedback/table/renderSortableHeaderCell'; import useQueryBasedColumnResize from 'sentry/components/feedback/table/useQueryBasedColumnResize'; import useQueryBasedSorting from 'sentry/components/feedback/table/useQueryBasedSorting'; import GridEditable, {GridColumnOrder} from 'sentry/components/gridEditable'; import Link from 'sentry/components/links/link'; import TextOverflow from 'sentry/components/textOverflow'; import {IconCursorArrow} from 'sentry/icons'; import {space} from 'sentry/styles/space'; import {Organization} from 'sentry/types'; import useOrganization from 'sentry/utils/useOrganization'; import {normalizeUrl} from 'sentry/utils/withDomainRequired'; import {DeadRageSelectorItem} from 'sentry/views/replays/types'; export interface UrlState { widths: string[]; } export function getAriaLabel(str: string) { const pre = str.split('aria="')[1]; if (!pre) { return ''; } return pre.substring(0, pre.lastIndexOf('"]')); } export function hydratedSelectorData(data, clickType?): DeadRageSelectorItem[] { return data.map(d => ({ ...(clickType ? {[clickType]: d[clickType]} : { count_dead_clicks: d.count_dead_clicks, count_rage_clicks: d.count_rage_clicks, }), dom_element: d.dom_element, element: d.dom_element.split(/[#.]+/)[0], aria_label: getAriaLabel(d.dom_element), })); } interface Props { clickCountColumns: {key: string; name: string}[]; clickCountSortable: boolean; data: DeadRageSelectorItem[]; isError: boolean; isLoading: boolean; location: Location; customHandleResize?: () => void; title?: ReactNode; } const BASE_COLUMNS: GridColumnOrder[] = [ {key: 'element', name: 'element'}, {key: 'dom_element', name: 'selector'}, {key: 'aria_label', name: 'aria label'}, ]; export default function SelectorTable({ clickCountColumns, data, isError, isLoading, location, title, customHandleResize, clickCountSortable, }: Props) { const organization = useOrganization(); const {currentSort, makeSortLinkGenerator} = useQueryBasedSorting({ defaultSort: {field: clickCountColumns[0].key, kind: 'desc'}, location, }); const {columns, handleResizeColumn} = useQueryBasedColumnResize({ columns: BASE_COLUMNS.concat(clickCountColumns), location, }); const renderHeadCell = useMemo( () => renderSortableHeaderCell({ currentSort, makeSortLinkGenerator, onClick: () => {}, rightAlignedColumns: [], sortableColumns: clickCountSortable ? clickCountColumns : [], }), [currentSort, makeSortLinkGenerator, clickCountColumns, clickCountSortable] ); const renderBodyCell = useCallback( (column, dataRow) => { const value = dataRow[column.key]; switch (column.key) { case 'dom_element': return ; case 'element': case 'aria_label': return ( {value} ); default: return renderSimpleBodyCell(column, dataRow); } }, [organization] ); return ( } title={title} /> ); } function SelectorLink({ organization, value, }: { organization: Organization; value: string; }) { return ( {value} ); } function renderSimpleBodyCell(column: GridColumnOrder, dataRow: T) { if (column.key === 'count_dead_clicks') { return ( {dataRow[column.key]} ); } if (column.key === 'count_rage_clicks') { return ( {dataRow[column.key]} ); } return {dataRow[column.key]}; } const DeadClickCount = styled(TextOverflow)` color: ${p => p.theme.yellow300}; `; const RageClickCount = styled(TextOverflow)` color: ${p => p.theme.red300}; `; const IconContainer = styled('span')` margin-right: ${space(1)}; `;