Skip to content

Commit 6b2607b

Browse files
ref(logs): Deduplicate cell action components and use design system
- Export CellActionContainer and CellActionMenuTrigger from cellAction.tsx instead of duplicating them in logsTableRow.tsx - Extract EllipsisActionMenu as a shared presentational component for the ellipsis dropdown pattern used by both CellAction and LogCellAction - Replace inline icon children with Button's built-in icon prop (4 instances) - Replace custom LogDetailTableActionsButtonBar with Flex gap="xl" Co-Authored-By: Claude Sonnet 4 <noreply@anthropic.com> Made-with: Cursor
1 parent bde7606 commit 6b2607b

File tree

2 files changed

+52
-106
lines changed

2 files changed

+52
-106
lines changed

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

Lines changed: 32 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -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+
export 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/tables/logsTableRow.tsx

Lines changed: 20 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -10,25 +10,16 @@ import React, {
1010
useState,
1111
} from 'react';
1212
import {useTheme} from '@emotion/react';
13-
import styled from '@emotion/styled';
1413
import classNames from 'classnames';
1514
import omit from 'lodash/omit';
1615

1716
import {Button} from '@sentry/scraps/button';
1817
import {Flex} from '@sentry/scraps/layout';
1918

20-
import {DropdownMenu} from 'sentry/components/dropdownMenu';
2119
import {EmptyStreamWrapper} from 'sentry/components/emptyStateWarning';
2220
import ProjectBadge from 'sentry/components/idBadge/projectBadge';
2321
import {LoadingIndicator} from 'sentry/components/loadingIndicator';
24-
import {
25-
IconAdd,
26-
IconCopy,
27-
IconEllipsis,
28-
IconJson,
29-
IconSubtract,
30-
IconWarning,
31-
} from 'sentry/icons';
22+
import {IconAdd, IconCopy, IconJson, IconSubtract, IconWarning} from 'sentry/icons';
3223
import {IconChevron} from 'sentry/icons/iconChevron';
3324
import {t} from 'sentry/locale';
3425
import {defined} from 'sentry/utils';
@@ -44,6 +35,10 @@ import {useLocation} from 'sentry/utils/useLocation';
4435
import {useOrganization} from 'sentry/utils/useOrganization';
4536
import {useProjectFromId} from 'sentry/utils/useProjectFromId';
4637
import {useProjects} from 'sentry/utils/useProjects';
38+
import {
39+
CellActionContainer,
40+
EllipsisActionMenu,
41+
} from 'sentry/views/discover/table/cellAction';
4742
import {AttributesTree} from 'sentry/views/explore/components/traceItemAttributes/attributesTree';
4843
import {
4944
useLogsAutoRefreshEnabled,
@@ -74,7 +69,6 @@ import {
7469
DetailsWrapper,
7570
getLogColors,
7671
LogAttributeTreeWrapper,
77-
LogDetailTableActionsButtonBar,
7872
LogDetailTableActionsCell,
7973
LogDetailTableBodyCell,
8074
LogFirstCellContent,
@@ -578,26 +572,26 @@ function LogRowDetails({
578572
}
579573

580574
function LogRowDetailsFilterActions({tableDataRow}: {tableDataRow: LogTableRowItem}) {
581-
const theme = useTheme();
582575
const addSearchFilter = useAddSearchFilter();
583576
return (
584-
<LogDetailTableActionsButtonBar>
577+
<Flex gap="xl">
585578
<Button
586579
priority="link"
587580
size="sm"
581+
icon={<IconAdd />}
588582
onClick={() => {
589583
addSearchFilter({
590584
key: OurLogKnownFieldKey.MESSAGE,
591585
value: tableDataRow[OurLogKnownFieldKey.MESSAGE],
592586
});
593587
}}
594588
>
595-
<IconAdd size="md" style={{paddingRight: theme.space.xs}} />
596589
{t('Add to filter')}
597590
</Button>
598591
<Button
599592
priority="link"
600593
size="sm"
594+
icon={<IconSubtract />}
601595
onClick={() => {
602596
addSearchFilter({
603597
key: OurLogKnownFieldKey.MESSAGE,
@@ -606,10 +600,9 @@ function LogRowDetailsFilterActions({tableDataRow}: {tableDataRow: LogTableRowIt
606600
});
607601
}}
608602
>
609-
<IconSubtract size="md" style={{paddingRight: theme.space.xs}} />
610603
{t('Exclude from filter')}
611604
</Button>
612-
</LogDetailTableActionsButtonBar>
605+
</Flex>
613606
);
614607
}
615608

@@ -620,7 +613,6 @@ function LogRowDetailsActions({
620613
fullLogDataResult: UseApiQueryResult<TraceItemDetailsResponse, RequestError>;
621614
tableDataRow: LogTableRowItem;
622615
}) {
623-
const theme = useTheme();
624616
const {data, isPending, isError} = fullLogDataResult;
625617
const isFrozen = useLogsFrozenIsFrozen();
626618
const organization = useOrganization();
@@ -666,21 +658,26 @@ function LogRowDetailsActions({
666658
) : (
667659
<span />
668660
)}
669-
<LogDetailTableActionsButtonBar>
670-
<Button priority="link" size="sm" onClick={copyText} disabled={!message}>
671-
<IconCopy size="md" style={{paddingRight: theme.space.sm}} />
661+
<Flex gap="xl">
662+
<Button
663+
priority="link"
664+
size="sm"
665+
icon={<IconCopy />}
666+
onClick={copyText}
667+
disabled={!message}
668+
>
672669
{t('Copy message')}
673670
</Button>
674671
<Button
675672
priority="link"
676673
size="sm"
674+
icon={<IconJson />}
677675
onClick={copyAsJson}
678676
disabled={isPending || isError || !json}
679677
>
680-
<IconJson size="md" style={{paddingRight: theme.space.xs}} />
681678
{t('Copy as JSON')}
682679
</Button>
683-
</LogDetailTableActionsButtonBar>
680+
</Flex>
684681
</Fragment>
685682
);
686683
}
@@ -784,58 +781,7 @@ function LogCellAction({
784781
return (
785782
<CellActionContainer>
786783
{children}
787-
<DropdownMenu
788-
items={items}
789-
usePortal
790-
size="sm"
791-
offset={4}
792-
position="bottom"
793-
preventOverflowOptions={{padding: 4}}
794-
flipOptions={{
795-
fallbackPlacements: [
796-
'top',
797-
'right-start',
798-
'right-end',
799-
'left-start',
800-
'left-end',
801-
],
802-
}}
803-
trigger={triggerProps => (
804-
<CellActionMenuTrigger
805-
{...triggerProps}
806-
aria-label={t('Actions')}
807-
icon={<IconEllipsis size="xs" />}
808-
size="zero"
809-
/>
810-
)}
811-
/>
784+
<EllipsisActionMenu items={items} />
812785
</CellActionContainer>
813786
);
814787
}
815-
816-
const CellActionContainer = styled('div')`
817-
position: relative;
818-
width: 100%;
819-
height: 100%;
820-
display: flex;
821-
flex-direction: column;
822-
justify-content: center;
823-
`;
824-
825-
const CellActionMenuTrigger = styled(Button)`
826-
position: absolute;
827-
top: 50%;
828-
right: -1px;
829-
transform: translateY(-50%);
830-
padding: ${p => p.theme.space.xs};
831-
display: flex;
832-
align-items: center;
833-
opacity: 0;
834-
transition: opacity 0.1s;
835-
836-
&:focus-visible,
837-
&[aria-expanded='true'],
838-
${CellActionContainer}:hover & {
839-
opacity: 1;
840-
}
841-
`;

0 commit comments

Comments
 (0)