Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 9 additions & 4 deletions static/app/utils/discover/fieldRenderers.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -322,15 +322,19 @@ export const FIELD_FORMATTERS: FieldFormatters = {
},
string: {
isSortable: true,
renderFunc: (field, data) => {
renderFunc: (field, data, baggage) => {
// Some fields have long arrays in them, only show the tail of the data.
const value = Array.isArray(data[field])
? data[field].slice(-1)
: defined(data[field])
? data[field]
: emptyValue;

if (isUrl(value)) {
// In the future, external linking will be done through CellAction component instead of the default renderer
if (
!baggage?.organization.features.includes('discover-cell-actions-v2') &&
isUrl(value)
) {
return (
<Tooltip title={value} containerDisplayMode="block" showOnlyOnOverflow>
<Container>
Expand Down Expand Up @@ -496,7 +500,7 @@ const SPECIAL_FIELDS: Record<string, SpecialField> = {
},
'span.description': {
sortField: 'span.description',
renderFunc: data => {
renderFunc: (data, {organization}) => {
const value = data[SpanFields.SPAN_DESCRIPTION];
const op: string = data[SpanFields.SPAN_OP];
const projectId =
Expand Down Expand Up @@ -524,7 +528,8 @@ const SPECIAL_FIELDS: Record<string, SpecialField> = {
maxWidth={400}
>
<Container>
{isUrl(value) ? (
{!organization.features.includes('discover-cell-actions-v2') &&
isUrl(value) ? (
<ExternalLink href={value}>{value}</ExternalLink>
) : (
nullableValue(value)
Expand Down
78 changes: 59 additions & 19 deletions static/app/views/discover/table/cellAction.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,12 @@ import {t, tct} from 'sentry/locale';
import {defined} from 'sentry/utils';
import type {TableDataRow} from 'sentry/utils/discover/discoverQuery';
import {
fieldAlignment,
isEquationAlias,
isRelativeSpanOperationBreakdownField,
} from 'sentry/utils/discover/fields';
import getDuration from 'sentry/utils/duration/getDuration';
import {isUrl} from 'sentry/utils/string/isUrl';
import type {MutableSearch} from 'sentry/utils/tokenizeSearch';
import useOrganization from 'sentry/utils/useOrganization';

Expand All @@ -27,19 +29,7 @@ export enum Actions {
DRILLDOWN = 'drilldown',
EDIT_THRESHOLD = 'edit_threshold',
COPY_TO_CLIPBOARD = 'copy_to_clipboard',
}

export function copyToClipBoard(data: any) {
function stringifyValue(value: any): string {
if (!value) return '';
if (typeof value !== 'object') {
return value.toString();
}
return JSON.stringify(value) ?? value.toString();
}
navigator.clipboard.writeText(stringifyValue(data)).catch(_ => {
addErrorMessage('Error copying to clipboard');
});
OPEN_EXTERNAL_LINK = 'open_external_link',
}

export function updateQuery(
Expand Down Expand Up @@ -98,7 +88,10 @@ export function updateQuery(
// these actions do not modify the query in any way,
// instead they have side effects
case Actions.COPY_TO_CLIPBOARD:
copyToClipBoard(value);
copyToClipboard(value);
break;
case Actions.OPEN_EXTERNAL_LINK:
openExternalLink(value);
break;
case Actions.RELEASE:
case Actions.DRILLDOWN:
Expand Down Expand Up @@ -151,6 +144,35 @@ export function excludeFromFilter(
oldFilter.addFilterValues(negation, value);
}

/**
* Copies the provided value to a user's clipboard.
* @param value
*/
export function copyToClipboard(value: string | number | string[]) {
function stringifyValue(val: string | number | string[]): string {
if (!val) return '';
if (typeof val !== 'object') {
return val.toString();
}
return JSON.stringify(val) ?? val.toString();
}
navigator.clipboard.writeText(stringifyValue(value)).catch(_ => {
addErrorMessage('Error copying to clipboard');
});
}

/**
* If provided value is a valid url, opens the url in a new tab
* @param value
*/
export function openExternalLink(value: string | number | string[]) {
if (typeof value === 'string' && isUrl(value)) {
window.open(value, '_blank', 'noopener,noreferrer');
} else {
addErrorMessage('Could not open link');
}
}

type CellActionsOpts = {
column: TableColumn<keyof TableDataRow>;
dataRow: TableDataRow;
Expand Down Expand Up @@ -251,21 +273,38 @@ function makeCellActions({

if (value) addMenuItem(Actions.COPY_TO_CLIPBOARD, t('Copy to clipboard'));

if (isUrl(value)) addMenuItem(Actions.OPEN_EXTERNAL_LINK, t('Open external link'));

if (actions.length === 0) {
return null;
}

return actions;
}

type Props = React.PropsWithoutRef<CellActionsOpts>;
/**
* Potentially temporary as design and product need more time to determine how logs table should trigger the dropdown.
* Currently, the agreed default for every table should be bold hover. Logs is the only table to use the ellipsis trigger.
*/
export enum ActionTriggerType {
ELLIPSIS = 'ellipsis',
BOLD_HOVER = 'bold_hover',
}

function CellAction(props: Props) {
type Props = React.PropsWithoutRef<CellActionsOpts> & {
triggerType?: ActionTriggerType;
};

function CellAction({triggerType = ActionTriggerType.BOLD_HOVER, ...props}: Props) {
const organization = useOrganization();
const {children} = props;
const {children, column} = props;
const cellActions = makeCellActions(props);
const align = fieldAlignment(column.key as string, column.type);

if (organization.features.includes('organizations:discover-cell-actions-v2'))
if (
organization.features.includes('discover-cell-actions-v2') &&
triggerType === ActionTriggerType.BOLD_HOVER
)
return (
<Container
data-test-id={cellActions === null ? undefined : 'cell-action-container'}
Expand All @@ -276,10 +315,11 @@ function CellAction(props: Props) {
usePortal
size="sm"
offset={4}
position="bottom-start"
position={align === 'left' ? 'bottom-start' : 'bottom-end'}
preventOverflowOptions={{padding: 4}}
flipOptions={{
fallbackPlacements: [
'bottom-start',
'bottom-end',
'top',
'right-start',
Expand Down
6 changes: 1 addition & 5 deletions static/app/views/discover/table/tableView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ import {makeReleasesPathname} from 'sentry/views/releases/utils/pathnames';

import {QuickContextHoverWrapper} from './quickContext/quickContextWrapper';
import {ContextType} from './quickContext/utils';
import CellAction, {Actions, copyToClipBoard, updateQuery} from './cellAction';
import CellAction, {Actions, updateQuery} from './cellAction';
import ColumnEditModal, {modalCss} from './columnEditModal';
import TableActions from './tableActions';
import {TopResultsIndicator} from './topResultsIndicator';
Expand Down Expand Up @@ -609,10 +609,6 @@ function TableView(props: TableViewProps) {

return;
}
case Actions.COPY_TO_CLIPBOARD: {
copyToClipBoard(value);
break;
}
default: {
// Some custom perf metrics have units.
// These custom perf metrics need to be adjusted to the correct value.
Expand Down
1 change: 1 addition & 0 deletions static/app/views/explore/components/table.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ export const ALLOWED_CELL_ACTIONS: Actions[] = [
Actions.SHOW_GREATER_THAN,
Actions.SHOW_LESS_THAN,
Actions.COPY_TO_CLIPBOARD,
Actions.OPEN_EXTERNAL_LINK,
];

const MINIMUM_COLUMN_WIDTH = COL_WIDTH_MINIMUM;
Expand Down
10 changes: 8 additions & 2 deletions static/app/views/explore/logs/tables/logsTableRow.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,9 @@ import useOrganization from 'sentry/utils/useOrganization';
import useProjectFromId from 'sentry/utils/useProjectFromId';
import CellAction, {
Actions,
copyToClipBoard,
ActionTriggerType,
copyToClipboard,
openExternalLink,
} from 'sentry/views/discover/table/cellAction';
import type {TableColumn} from 'sentry/views/discover/table/types';
import {AttributesTree} from 'sentry/views/explore/components/traceItemAttributes/attributesTree';
Expand Down Expand Up @@ -305,7 +307,10 @@ export const LogRowContent = memo(function LogRowContent({
});
break;
case Actions.COPY_TO_CLIPBOARD:
copyToClipBoard(cellValue);
copyToClipboard(cellValue);
break;
case Actions.OPEN_EXTERNAL_LINK:
openExternalLink(cellValue);
break;
default:
break;
Expand All @@ -316,6 +321,7 @@ export const LogRowContent = memo(function LogRowContent({
? []
: ALLOWED_CELL_ACTIONS
}
triggerType={ActionTriggerType.ELLIPSIS}
>
{renderedField}
</CellAction>
Expand Down
22 changes: 14 additions & 8 deletions static/app/views/explore/tables/fieldRenderer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import ProjectBadge from 'sentry/components/idBadge/projectBadge';
import ExternalLink from 'sentry/components/links/externalLink';
import TimeSince from 'sentry/components/timeSince';
import {space} from 'sentry/styles/space';
import type {Organization} from 'sentry/types/organization';
import type {Project} from 'sentry/types/project';
import {defined} from 'sentry/utils';
import type {TableDataRow} from 'sentry/utils/discover/discoverQuery';
Expand Down Expand Up @@ -122,7 +123,7 @@ function BaseExploreFieldRenderer({

const field = String(column.key);

const renderer = getExploreFieldRenderer(field, meta, projectsMap);
const renderer = getExploreFieldRenderer(field, meta, projectsMap, organization);

let rendered = renderer(data, {
location,
Expand All @@ -146,7 +147,7 @@ function BaseExploreFieldRenderer({
source: TraceViewSources.TRACES,
});

rendered = <Link to={target}>{rendered}</Link>;
return <Link to={target}>{rendered}</Link>;
}

if (['id', 'span_id', 'transaction.id'].includes(field)) {
Expand All @@ -162,7 +163,7 @@ function BaseExploreFieldRenderer({
source: TraceViewSources.TRACES,
});

rendered = <Link to={target}>{rendered}</Link>;
return <Link to={target}>{rendered}</Link>;
}

if (field === 'profile.id') {
Expand All @@ -171,7 +172,7 @@ function BaseExploreFieldRenderer({
projectSlug: data.project,
profileId: data['profile.id'],
});
rendered = <Link to={target}>{rendered}</Link>;
return <Link to={target}>{rendered}</Link>;
}

return (
Expand All @@ -192,13 +193,14 @@ function BaseExploreFieldRenderer({
function getExploreFieldRenderer(
field: string,
meta: MetaType,
projects: Record<string, Project>
projects: Record<string, Project>,
organization: Organization
): ReturnType<typeof getFieldRenderer> {
if (field === 'id' || field === 'span_id') {
return eventIdRenderFunc(field);
}
if (field === 'span.description') {
return spanDescriptionRenderFunc(projects);
return spanDescriptionRenderFunc(projects, organization);
}
return getFieldRenderer(field, meta, false);
}
Expand All @@ -215,7 +217,10 @@ function eventIdRenderFunc(field: string) {
return renderer;
}

function spanDescriptionRenderFunc(projects: Record<string, Project>) {
function spanDescriptionRenderFunc(
projects: Record<string, Project>,
organization: Organization
) {
function renderer(data: EventData) {
const project = projects[data.project];

Expand All @@ -239,7 +244,8 @@ function spanDescriptionRenderFunc(projects: Record<string, Project>) {
/>
)}
<WrappingText>
{isUrl(value) ? (
{!organization.features.includes('discover-cell-actions-v2') &&
isUrl(value) ? (
<ExternalLink href={value}>{value}</ExternalLink>
) : (
nullableValue(value)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,7 @@ function EventsTable({
Actions.EXCLUDE,
Actions.SHOW_GREATER_THAN,
Actions.SHOW_LESS_THAN,
Actions.OPEN_EXTERNAL_LINK,
];

if (['attachments', 'minidump'].includes(field)) {
Expand Down
Loading