Skip to content

Commit ac1080c

Browse files
feat(logs): Add "Copy message" and "Copy as JSON" to log row actions
Replace the single "Copy to clipboard" option in the log row ellipsis dropdown with two distinct actions: "Copy message" (copies the cell value) and "Copy as JSON" (copies the full log payload as formatted JSON). The JSON option is always clickable and defers the copy until data finishes loading if needed. Also add a "Copy message" button alongside the existing "Copy as JSON" button in the expanded row details view. Refs LINEAR-LOGS-637 Co-Authored-By: Claude Sonnet 4 <noreply@anthropic.com> Made-with: Cursor
1 parent 492a2ed commit ac1080c

File tree

4 files changed

+190
-110
lines changed

4 files changed

+190
-110
lines changed

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

Lines changed: 33 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -327,7 +327,7 @@ function getInternalLinkActionLabel(field: string): string {
327327
* Potentially temporary as design and product need more time to determine how logs table should trigger the dropdown.
328328
* Currently, the agreed default for every table should be bold hover. Logs is the only table to use the ellipsis trigger.
329329
*/
330-
export enum ActionTriggerType {
330+
enum ActionTriggerType {
331331
ELLIPSIS = 'ellipsis',
332332
BOLD_HOVER = 'bold_hover',
333333
}
@@ -357,7 +357,7 @@ export function CellAction({
357357

358358
if (triggerType === ActionTriggerType.BOLD_HOVER) {
359359
return (
360-
<Container
360+
<CellActionContainer
361361
data-test-id={cellActions === null ? undefined : 'cell-action-container'}
362362
>
363363
{cellActions?.length ? (
@@ -413,45 +413,45 @@ export function CellAction({
413413
) : (
414414
children
415415
)}
416-
</Container>
416+
</CellActionContainer>
417417
);
418418
}
419419

420420
return (
421-
<Container data-test-id={cellActions === null ? undefined : 'cell-action-container'}>
421+
<CellActionContainer
422+
data-test-id={cellActions === null ? undefined : 'cell-action-container'}
423+
>
422424
{children}
423-
{cellActions?.length && (
424-
<DropdownMenu
425-
items={cellActions}
426-
usePortal
427-
size="sm"
428-
offset={4}
429-
position="bottom"
430-
preventOverflowOptions={{padding: 4}}
431-
flipOptions={{
432-
fallbackPlacements: [
433-
'top',
434-
'right-start',
435-
'right-end',
436-
'left-start',
437-
'left-end',
438-
],
439-
}}
440-
trigger={triggerProps => (
441-
<ActionMenuTrigger
442-
{...triggerProps}
443-
aria-label={t('Actions')}
444-
icon={<IconEllipsis size="xs" />}
445-
size="zero"
446-
/>
447-
)}
425+
{cellActions?.length && <EllipsisActionMenu items={cellActions} />}
426+
</CellActionContainer>
427+
);
428+
}
429+
430+
export function EllipsisActionMenu({items}: {items: MenuItemProps[]}) {
431+
return (
432+
<DropdownMenu
433+
items={items}
434+
usePortal
435+
size="sm"
436+
offset={4}
437+
position="bottom"
438+
preventOverflowOptions={{padding: 4}}
439+
flipOptions={{
440+
fallbackPlacements: ['top', 'right-start', 'right-end', 'left-start', 'left-end'],
441+
}}
442+
trigger={triggerProps => (
443+
<CellActionMenuTrigger
444+
{...triggerProps}
445+
aria-label={t('Actions')}
446+
icon={<IconEllipsis size="xs" />}
447+
size="zero"
448448
/>
449449
)}
450-
</Container>
450+
/>
451451
);
452452
}
453453

454-
const Container = styled('div')`
454+
export const CellActionContainer = styled('div')`
455455
position: relative;
456456
width: 100%;
457457
height: 100%;
@@ -460,7 +460,7 @@ const Container = styled('div')`
460460
justify-content: center;
461461
`;
462462

463-
const ActionMenuTrigger = styled(Button)`
463+
const CellActionMenuTrigger = styled(Button)`
464464
position: absolute;
465465
top: 50%;
466466
right: -1px;
@@ -474,7 +474,7 @@ const ActionMenuTrigger = styled(Button)`
474474
transition: opacity 0.1s;
475475
&:focus-visible,
476476
&[aria-expanded='true'],
477-
${Container}:hover & {
477+
${CellActionContainer}:hover & {
478478
opacity: 1;
479479
}
480480
`;

static/app/views/explore/logs/styles.tsx

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -160,14 +160,6 @@ export const LogDetailTableActionsCell = styled(TableBodyCell)`
160160
padding: ${p => p.theme.space.xs} ${p => p.theme.space.xl};
161161
}
162162
`;
163-
export const LogDetailTableActionsButtonBar = styled('div')`
164-
display: flex;
165-
gap: ${p => p.theme.space.md};
166-
& button {
167-
font-weight: ${p => p.theme.font.weight.sans.regular};
168-
}
169-
`;
170-
171163
export const DetailsWrapper = styled('tr')`
172164
align-items: center;
173165
background-color: ${p => p.theme.colors.gray100};

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -522,9 +522,9 @@ describe('logsTableRow', () => {
522522
const actionsButton = screen.getAllByRole('button', {name: 'Actions'})[0]!;
523523
await userEvent.click(actionsButton);
524524

525-
// Click "Copy to clipboard" in the dropdown menu
525+
// Click "Copy message" in the dropdown menu
526526
const copyItem = await screen.findByRole('menuitemradio', {
527-
name: 'Copy to clipboard',
527+
name: 'Copy message',
528528
});
529529
await userEvent.click(copyItem);
530530

0 commit comments

Comments
 (0)