Skip to content

Commit c471f0b

Browse files
authored
feat(explore): add copy to clipboard to cell actions (#93907)
### Changes Add a copy to clipboard option to the tables that use the cell action dropdown. Option does not render if the value is empty/undefined. Currently only enabled for tables in explore. ### Video https://github.com/user-attachments/assets/b1b06b33-c566-40ff-9986-19c30fa7d9e2
1 parent 315b438 commit c471f0b

File tree

4 files changed

+38
-3
lines changed

4 files changed

+38
-3
lines changed

static/app/views/discover/table/cellAction.tsx

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import {Component} from 'react';
22
import styled from '@emotion/styled';
33

4+
import {addErrorMessage} from 'sentry/actionCreators/indicator';
45
import {Button} from 'sentry/components/core/button';
56
import type {MenuItemProps} from 'sentry/components/dropdownMenu';
67
import {DropdownMenu} from 'sentry/components/dropdownMenu';
@@ -26,6 +27,20 @@ export enum Actions {
2627
RELEASE = 'release',
2728
DRILLDOWN = 'drilldown',
2829
EDIT_THRESHOLD = 'edit_threshold',
30+
COPY_TO_CLIPBOARD = 'copy_to_clipboard',
31+
}
32+
33+
export function copyToClipBoard(data: any) {
34+
function stringifyValue(value: any): string {
35+
if (!value) return '';
36+
if (typeof value !== 'object') {
37+
return value.toString();
38+
}
39+
return JSON.stringify(value) ?? value.toString();
40+
}
41+
navigator.clipboard.writeText(stringifyValue(data)).catch(_ => {
42+
addErrorMessage('Error copying to clipboard');
43+
});
2944
}
3045

3146
export function updateQuery(
@@ -83,6 +98,9 @@ export function updateQuery(
8398
}
8499
// these actions do not modify the query in any way,
85100
// instead they have side effects
101+
case Actions.COPY_TO_CLIPBOARD:
102+
copyToClipBoard(value);
103+
break;
86104
case Actions.RELEASE:
87105
case Actions.DRILLDOWN:
88106
break;
@@ -232,6 +250,8 @@ function makeCellActions({
232250
);
233251
}
234252

253+
if (value) addMenuItem(Actions.COPY_TO_CLIPBOARD, t('Copy to clipboard'));
254+
235255
if (actions.length === 0) {
236256
return null;
237257
}

static/app/views/discover/table/tableView.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ import {makeReleasesPathname} from 'sentry/views/releases/utils/pathnames';
6666

6767
import {QuickContextHoverWrapper} from './quickContext/quickContextWrapper';
6868
import {ContextType} from './quickContext/utils';
69-
import CellAction, {Actions, updateQuery} from './cellAction';
69+
import CellAction, {Actions, copyToClipBoard, updateQuery} from './cellAction';
7070
import ColumnEditModal, {modalCss} from './columnEditModal';
7171
import TableActions from './tableActions';
7272
import {TopResultsIndicator} from './topResultsIndicator';
@@ -623,6 +623,10 @@ function TableView(props: TableViewProps) {
623623

624624
return;
625625
}
626+
case Actions.COPY_TO_CLIPBOARD: {
627+
copyToClipBoard(value);
628+
break;
629+
}
626630
default: {
627631
// Some custom perf metrics have units.
628632
// These custom perf metrics need to be adjusted to the correct value.

static/app/views/explore/components/table.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ export const ALLOWED_CELL_ACTIONS: Actions[] = [
5353
Actions.EXCLUDE,
5454
Actions.SHOW_GREATER_THAN,
5555
Actions.SHOW_LESS_THAN,
56+
Actions.COPY_TO_CLIPBOARD,
5657
];
5758

5859
const MINIMUM_COLUMN_WIDTH = COL_WIDTH_MINIMUM;

static/app/views/explore/logs/tables/logsTableRow.tsx

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,10 @@ import {FieldValueType} from 'sentry/utils/fields';
1515
import {useLocation} from 'sentry/utils/useLocation';
1616
import useOrganization from 'sentry/utils/useOrganization';
1717
import useProjectFromId from 'sentry/utils/useProjectFromId';
18-
import CellAction, {Actions} from 'sentry/views/discover/table/cellAction';
18+
import CellAction, {
19+
Actions,
20+
copyToClipBoard,
21+
} from 'sentry/views/discover/table/cellAction';
1922
import type {TableColumn} from 'sentry/views/discover/table/types';
2023
import {AttributesTree} from 'sentry/views/explore/components/traceItemAttributes/attributesTree';
2124
import {
@@ -74,7 +77,11 @@ type LogsRowProps = {
7477
onExpandHeight?: (logItemId: string, estimatedHeight: number) => void;
7578
};
7679

77-
const ALLOWED_CELL_ACTIONS: Actions[] = [Actions.ADD, Actions.EXCLUDE];
80+
const ALLOWED_CELL_ACTIONS: Actions[] = [
81+
Actions.ADD,
82+
Actions.EXCLUDE,
83+
Actions.COPY_TO_CLIPBOARD,
84+
];
7885

7986
function isInsideButton(element: Element | null): boolean {
8087
let i = 10;
@@ -298,6 +305,9 @@ export const LogRowContent = memo(function LogRowContent({
298305
negated: true,
299306
});
300307
break;
308+
case Actions.COPY_TO_CLIPBOARD:
309+
copyToClipBoard(cellValue);
310+
break;
301311
default:
302312
break;
303313
}

0 commit comments

Comments
 (0)